@proveanything/smartlinks 2.0.26 → 2.0.29

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.
@@ -26,6 +26,11 @@ export declare const AI_TOOL_NAMES: {
26
26
  readonly PDF_CREATE: "pdf.create";
27
27
  readonly PDF_FILL: "pdf.fill";
28
28
  readonly PDF_MERGE: "pdf.merge";
29
+ readonly PDF_INSPECT: "pdf.inspect";
30
+ readonly PDF_RENDER: "pdf.render";
31
+ readonly PDF_EXTRACT: "pdf.extract";
32
+ readonly PDF_DECODE_BARCODES: "pdf.decodeBarcodes";
33
+ readonly PDF_INSPECT_GRAPHICS: "pdf.inspectGraphics";
29
34
  readonly HTTP_REQUEST: "http.request";
30
35
  readonly TRANSLATE: "translate";
31
36
  };
package/dist/ai-tools.js CHANGED
@@ -23,6 +23,11 @@ export const AI_TOOL_NAMES = {
23
23
  PDF_CREATE: 'pdf.create',
24
24
  PDF_FILL: 'pdf.fill',
25
25
  PDF_MERGE: 'pdf.merge',
26
+ PDF_INSPECT: 'pdf.inspect',
27
+ PDF_RENDER: 'pdf.render',
28
+ PDF_EXTRACT: 'pdf.extract',
29
+ PDF_DECODE_BARCODES: 'pdf.decodeBarcodes',
30
+ PDF_INSPECT_GRAPHICS: 'pdf.inspectGraphics',
26
31
  HTTP_REQUEST: 'http.request',
27
32
  TRANSLATE: 'translate',
28
33
  };
@@ -58,6 +63,16 @@ export const BUILTIN_AI_TOOLS = [
58
63
  description: 'Fill an AcroForm PDF\'s fields from a { field: value } map → hosted URL.' },
59
64
  { name: 'pdf.merge', group: 'media', capabilities: ['media:pdf'],
60
65
  description: 'Merge several PDFs (by URL) into one → hosted URL.' },
66
+ { name: 'pdf.inspect', group: 'media', capabilities: ['web:read'],
67
+ description: 'Cheaply introspect a PDF before any AI call: page count/sizes, whether each page has an extractable text layer, whether a raster image is present, and a routing recommendation (text-native → cheap text pass; curve-only/raster → vision/OCR). Deterministic, no AI.' },
68
+ { name: 'pdf.render', group: 'media', capabilities: ['web:read'],
69
+ description: 'Rasterize a single PDF page to a PNG at a controllable DPI (e.g. 300 for small print) and return a hosted image URL — feed to a vision call or screenshot a page.' },
70
+ { name: 'pdf.extract', group: 'media', capabilities: ['web:read', 'ai:vision'],
71
+ description: 'Extract typed JSON from a PDF in one call given a schema and/or prompt. Auto-routes: text-layer pages use cheap text extraction; curve-only/raster pages are rendered and read with vision. Optional per-field confidence/source (includeConfidence) and bounding boxes (includeBoxes).' },
72
+ { name: 'pdf.decodeBarcodes', group: 'media', capabilities: ['web:read'],
73
+ description: 'Deterministically decode 1D/2D barcodes (EAN/UPC/QR/Code128/DataMatrix/…) on a PDF page by rasterizing and running a WASM decoder — reliable barcode/QR values where vision hallucinates. Returns value, symbology, page, bbox, confidence. No AI.' },
74
+ { name: 'pdf.inspectGraphics', group: 'media', capabilities: ['web:read'],
75
+ description: 'Prepress structural inspection: per-page vector-path/image/outlined-text counts + colour spaces used, plus document-level named SPOT colours (e.g. "PANTONE 871 C"). Answers whether spot/foil plates survived. Deterministic, no AI.' },
61
76
  { name: 'http.request', group: 'net', capabilities: ['net:http'],
62
77
  description: 'SSRF-guarded outbound HTTP(S) request to a public URL; returns status/headers/body.' },
63
78
  { name: 'translate', group: 'text', capabilities: ['ai:text'],
package/dist/api/ai.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ContentPart, FunctionCall, ToolCall, ChatMessage, ToolDefinition, ResponseTool, ResponseInputItem, ResponsesRequest, ResponsesResult, ResponsesStreamEvent, ChatCompletionRequest, ChatCompletionChoice, ChatCompletionResponse, ChatCompletionChunk, AIModel, AIModelListParams, AIModelListResponse, AgentRunRequest, AgentRunResult, AgentToolsQuery, AgentToolsResponse, SkillsListResponse, CatalogResponse, DocumentChunk, IndexDocumentRequest, IndexDocumentResponse, ConfigureAssistantRequest, ConfigureAssistantResponse, PublicChatRequest, PublicChatResponse, Session, RateLimitStatus, SessionStatistics, AiSession, AiSessionCreate, AiUsageReport, VoiceSessionRequest, VoiceSessionResponse, EphemeralTokenRequest, EphemeralTokenResponse, TranscriptionResponse, TTSRequest, GeneratePodcastRequest, PodcastScript, GeneratePodcastResponse, PodcastStatus, AIGenerateContentRequest, AIGenerateContentCandidate, AIGenerateContentResponse, AIGenerateImageRequest, AIGenerateImageResponse, AIGeneratedImage, AISearchPhotosRequest, AISearchPhotosPhoto, AISearchPhotosResponse, AIUploadedFile, AICacheRef } from "../types/ai.js";
1
+ import type { ContentPart, FunctionCall, ToolCall, ChatMessage, ToolDefinition, ResponseTool, ResponseInputItem, ResponsesRequest, ResponsesResult, ResponsesStreamEvent, ChatCompletionRequest, ChatCompletionChoice, ChatCompletionResponse, ChatCompletionChunk, AIModel, AIModelListParams, AIModelListResponse, AgentRunRequest, AgentRunResult, AgentToolsQuery, AgentToolsResponse, ToolRunResult, AiToolName, AiToolArgsMap, PublicAgentRunRequest, ClientTool, RunWithClientToolsOptions, SkillsListResponse, CatalogResponse, DocumentChunk, IndexDocumentRequest, IndexDocumentResponse, ConfigureAssistantRequest, ConfigureAssistantResponse, PublicChatRequest, PublicChatResponse, Session, RateLimitStatus, SessionStatistics, AiSession, AiSessionCreate, AiUsageReport, VoiceSessionRequest, VoiceSessionResponse, EphemeralTokenRequest, EphemeralTokenResponse, TranscriptionResponse, TTSRequest, GeneratePodcastRequest, PodcastScript, GeneratePodcastResponse, PodcastStatus, AIGenerateContentRequest, AIGenerateContentCandidate, AIGenerateContentResponse, AIGenerateImageRequest, AIGenerateImageResponse, AIGeneratedImage, AISearchPhotosRequest, AISearchPhotosPhoto, AISearchPhotosResponse, AIUploadedFile, AICacheRef } from "../types/ai.js";
2
2
  export type { ContentPart, FunctionCall, ToolCall, ChatMessage, ToolDefinition, ResponseTool, ResponseInputItem, ResponsesRequest, ResponsesResult, ResponsesStreamEvent, ChatCompletionRequest, ChatCompletionChoice, ChatCompletionResponse, ChatCompletionChunk, AIModel, AIModelListParams, AIModelListResponse, DocumentChunk, IndexDocumentRequest, IndexDocumentResponse, ConfigureAssistantRequest, ConfigureAssistantResponse, PublicChatRequest, PublicChatResponse, Session, RateLimitStatus, SessionStatistics, AiSession, AiSessionCreate, AiUsageReport, VoiceSessionRequest, VoiceSessionResponse, EphemeralTokenRequest, EphemeralTokenResponse, TranscriptionResponse, TTSRequest, GeneratePodcastRequest, PodcastScript, GeneratePodcastResponse, PodcastStatus, AIGenerateContentRequest, AIGenerateContentCandidate, AIGenerateContentResponse, AIGenerateImageRequest, AIGenerateImageResponse, AIGeneratedImage, AISearchPhotosRequest, AISearchPhotosPhoto, AISearchPhotosResponse, AIUploadedFile, AICacheRef, };
3
3
  declare namespace aiInternal {
4
4
  namespace chat {
@@ -34,6 +34,20 @@ declare namespace aiInternal {
34
34
  */
35
35
  function listTools(collectionId: string, query?: AgentToolsQuery): Promise<AgentToolsResponse>;
36
36
  }
37
+ namespace tools {
38
+ /**
39
+ * Invoke ONE built-in server tool directly — no model in the loop. This is the
40
+ * "direct code" caller of the orchestration-neutral tool registry: the SAME tools the
41
+ * agent loop and the Responses `server_tools` path run, but called as a plain, typed,
42
+ * deterministic API. A front end can use it two ways: (1) call a tool straight as an
43
+ * API (e.g. `pdf.render` / `pdf.extract` behind a PDF UX), or (2) drive its OWN agent
44
+ * loop and execute each model tool-call here. Capability-gated server-side to the
45
+ * caller's grants (same blast-radius rules as the agent loop).
46
+ * POST /admin/collection/:collectionId/ai/tools/:name/run
47
+ */
48
+ function run<K extends AiToolName>(collectionId: string, name: K, args: AiToolArgsMap[K]): Promise<ToolRunResult>;
49
+ function run<T = any>(collectionId: string, name: string, args?: Record<string, any>): Promise<ToolRunResult<T>>;
50
+ }
37
51
  namespace skills {
38
52
  /** List the skills apps can invoke (name, description, input/output schema). */
39
53
  function list(collectionId: string): Promise<SkillsListResponse>;
@@ -135,6 +149,14 @@ declare namespace aiInternal {
135
149
  * Chat with product assistant (RAG)
136
150
  */
137
151
  function chat(collectionId: string, request: PublicChatRequest): Promise<PublicChatResponse>;
152
+ /**
153
+ * Public agent loop — run the orchestration-neutral tool loop on the consumer surface. Exposes an
154
+ * app's PUBLIC server functions (`agent.tool:true`, `visibility:'public'`) to a consumer assistant;
155
+ * built-in tools are opt-in by explicit `server_tools[]` allowlist only. The caller runs as the
156
+ * signed-in consumer ('owner', send the authKit bearer) or anonymous ('public').
157
+ * POST /public/collection/:collectionId/ai/agent/run
158
+ */
159
+ function agentRun(collectionId: string, body: PublicAgentRunRequest): Promise<AgentRunResult>;
138
160
  /**
139
161
  * Get session history
140
162
  */
@@ -218,6 +240,23 @@ declare namespace aiInternal {
218
240
  * Create or warm a cache for AI (admin)
219
241
  */
220
242
  function createCache(collectionId: string, params: any): Promise<AICacheRef>;
243
+ /**
244
+ * Declare a client tool + its browser-side handler. The `declaration` (name/description/input) is
245
+ * sent to the model; the `handler` runs in the page when the model calls it — so it can touch the
246
+ * DOM, the user's session, local state, etc. Pair with `runWithClientTools`.
247
+ */
248
+ function defineClientTool(name: string, spec: {
249
+ description?: string;
250
+ input?: Record<string, any>;
251
+ }, handler: (args: Record<string, any>) => any | Promise<any>): ClientTool;
252
+ /**
253
+ * Run an agent conversation that can call CLIENT tools, resolving them in the browser automatically.
254
+ * Drives the suspend/resume loop: call the agent → if it suspends on a client tool
255
+ * (`status:'requires_action'`), run the matching handler(s), append their outputs, and resubmit —
256
+ * until a final answer. Built-ins + app functions ride along via `toolbelt`. Only declared tools run
257
+ * (a call for an unknown tool throws), and each runs with the user's own auth.
258
+ */
259
+ function runWithClientTools(collectionId: string, opts: RunWithClientToolsOptions): Promise<AgentRunResult>;
221
260
  }
222
261
  export declare const ai: {
223
262
  chat: {
@@ -251,6 +290,7 @@ export declare const ai: {
251
290
  };
252
291
  public: {
253
292
  chat: typeof aiInternal.publicClient.chat;
293
+ agentRun: typeof aiInternal.publicClient.agentRun;
254
294
  getSession: typeof aiInternal.publicClient.getSession;
255
295
  clearSession: typeof aiInternal.publicClient.clearSession;
256
296
  getRateLimit: typeof aiInternal.publicClient.getRateLimit;
@@ -265,6 +305,11 @@ export declare const ai: {
265
305
  run: typeof aiInternal.agent.run;
266
306
  listTools: typeof aiInternal.agent.listTools;
267
307
  };
308
+ tools: {
309
+ run: typeof aiInternal.tools.run;
310
+ };
311
+ defineClientTool: typeof aiInternal.defineClientTool;
312
+ runWithClientTools: typeof aiInternal.runWithClientTools;
268
313
  skills: {
269
314
  list: typeof aiInternal.skills.list;
270
315
  run: typeof aiInternal.skills.run;
package/dist/api/ai.js CHANGED
@@ -90,6 +90,17 @@ var aiInternal;
90
90
  agent.listTools = listTools;
91
91
  })(agent = aiInternal.agent || (aiInternal.agent = {}));
92
92
  // ============================================================================
93
+ // Tools (direct, deterministic single-tool invocation — no model loop)
94
+ // ============================================================================
95
+ let tools;
96
+ (function (tools) {
97
+ async function run(collectionId, name, args = {}) {
98
+ const path = `/admin/collection/${encodeURIComponent(collectionId)}/ai/tools/${encodeURIComponent(name)}/run`;
99
+ return post(path, args);
100
+ }
101
+ tools.run = run;
102
+ })(tools = aiInternal.tools || (aiInternal.tools = {}));
103
+ // ============================================================================
93
104
  // Skills + Catalog (app-facing discovery)
94
105
  // ============================================================================
95
106
  let skills;
@@ -290,6 +301,18 @@ var aiInternal;
290
301
  return post(path, request);
291
302
  }
292
303
  publicClient.chat = chat;
304
+ /**
305
+ * Public agent loop — run the orchestration-neutral tool loop on the consumer surface. Exposes an
306
+ * app's PUBLIC server functions (`agent.tool:true`, `visibility:'public'`) to a consumer assistant;
307
+ * built-in tools are opt-in by explicit `server_tools[]` allowlist only. The caller runs as the
308
+ * signed-in consumer ('owner', send the authKit bearer) or anonymous ('public').
309
+ * POST /public/collection/:collectionId/ai/agent/run
310
+ */
311
+ async function agentRun(collectionId, body) {
312
+ const path = `/public/collection/${encodeURIComponent(collectionId)}/ai/agent/run`;
313
+ return post(path, body);
314
+ }
315
+ publicClient.agentRun = agentRun;
293
316
  /**
294
317
  * Get session history
295
318
  */
@@ -472,6 +495,52 @@ var aiInternal;
472
495
  return post(path, params);
473
496
  }
474
497
  aiInternal.createCache = createCache;
498
+ // ============================================================================
499
+ // Client tools (kind c) — front-end tools the model calls, executed in the page
500
+ // ============================================================================
501
+ /**
502
+ * Declare a client tool + its browser-side handler. The `declaration` (name/description/input) is
503
+ * sent to the model; the `handler` runs in the page when the model calls it — so it can touch the
504
+ * DOM, the user's session, local state, etc. Pair with `runWithClientTools`.
505
+ */
506
+ function defineClientTool(name, spec, handler) {
507
+ const declaration = { name, description: spec === null || spec === void 0 ? void 0 : spec.description, input: spec === null || spec === void 0 ? void 0 : spec.input };
508
+ return { declaration, handler };
509
+ }
510
+ aiInternal.defineClientTool = defineClientTool;
511
+ /**
512
+ * Run an agent conversation that can call CLIENT tools, resolving them in the browser automatically.
513
+ * Drives the suspend/resume loop: call the agent → if it suspends on a client tool
514
+ * (`status:'requires_action'`), run the matching handler(s), append their outputs, and resubmit —
515
+ * until a final answer. Built-ins + app functions ride along via `toolbelt`. Only declared tools run
516
+ * (a call for an unknown tool throws), and each runs with the user's own auth.
517
+ */
518
+ async function runWithClientTools(collectionId, opts) {
519
+ const { tools, surface = 'admin', maxRounds = 8 } = opts;
520
+ const byName = new Map(tools.map((t) => [t.declaration.name, t]));
521
+ const clientTools = tools.map((t) => t.declaration);
522
+ const toolbelt = Object.assign(Object.assign({}, (opts.toolbelt || {})), { clientTools });
523
+ let input = opts.input;
524
+ for (let round = 0; round < maxRounds; round++) {
525
+ const body = { input, instructions: opts.instructions, model: opts.model, maxSteps: opts.maxSteps, toolbelt };
526
+ const res = surface === 'public'
527
+ ? await publicClient.agentRun(collectionId, body)
528
+ : await agent.run(collectionId, body);
529
+ if (!res || res.status !== 'requires_action')
530
+ return res;
531
+ const outputs = [];
532
+ for (const call of (res.client_tool_calls || [])) {
533
+ const tool = byName.get(call.name);
534
+ if (!tool)
535
+ throw new Error(`runWithClientTools: model called undeclared client tool "${call.name}"`);
536
+ const out = await tool.handler(call.args || {});
537
+ outputs.push({ type: 'function_call_output', call_id: call.callId, output: typeof out === 'string' ? out : JSON.stringify(out !== null && out !== void 0 ? out : null) });
538
+ }
539
+ input = [...(res.items || []), ...outputs];
540
+ }
541
+ throw new Error('runWithClientTools: exceeded maxRounds without a final answer');
542
+ }
543
+ aiInternal.runWithClientTools = runWithClientTools;
475
544
  })(aiInternal || (aiInternal = {}));
476
545
  export const ai = {
477
546
  chat: {
@@ -505,6 +574,7 @@ export const ai = {
505
574
  },
506
575
  public: {
507
576
  chat: aiInternal.publicClient.chat,
577
+ agentRun: aiInternal.publicClient.agentRun,
508
578
  getSession: aiInternal.publicClient.getSession,
509
579
  clearSession: aiInternal.publicClient.clearSession,
510
580
  getRateLimit: aiInternal.publicClient.getRateLimit,
@@ -519,6 +589,11 @@ export const ai = {
519
589
  run: aiInternal.agent.run,
520
590
  listTools: aiInternal.agent.listTools,
521
591
  },
592
+ tools: {
593
+ run: aiInternal.tools.run,
594
+ },
595
+ defineClientTool: aiInternal.defineClientTool,
596
+ runWithClientTools: aiInternal.runWithClientTools,
522
597
  skills: {
523
598
  list: aiInternal.skills.list,
524
599
  run: aiInternal.skills.run,
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 2.0.26 | Generated: 2026-09-28T11:34:04.010Z
3
+ Version: 2.0.29 | Generated: 2026-09-29T10:33:46.286Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -594,6 +594,8 @@ interface ResponsesRequest {
594
594
  only?: AiToolName[]
595
595
  exclude?: AiToolName[]
596
596
  maxSteps?: number
597
+ app_functions?: AgentAppFunctionsOption
598
+ toolbelt?: AgentToolbelt
597
599
  }
598
600
  ```
599
601
 
@@ -1177,6 +1179,96 @@ interface AgentRunRequest {
1177
1179
  allowCapabilities?: string[]
1178
1180
  only?: string[]
1179
1181
  exclude?: string[]
1182
+ appFunctions?: AgentAppFunctionsOption
1183
+ toolbelt?: AgentToolbelt
1184
+ }
1185
+ ```
1186
+
1187
+ **AgentAppFunctionsOption** (interface)
1188
+ ```typescript
1189
+ interface AgentAppFunctionsOption {
1190
+ appId: string
1191
+ channel?: string
1192
+ only?: string[]
1193
+ }
1194
+ ```
1195
+
1196
+ **AgentToolbelt** (interface)
1197
+ ```typescript
1198
+ interface AgentToolbelt {
1199
+ builtins?: boolean | AiToolName[]
1200
+ appFunctions?: AgentAppFunctionsOption | AgentAppFunctionsOption[]
1201
+ clientTools?: ClientToolDeclaration[]
1202
+ }
1203
+ ```
1204
+
1205
+ **ClientToolDeclaration** (interface)
1206
+ ```typescript
1207
+ interface ClientToolDeclaration {
1208
+ name: string
1209
+ description?: string
1210
+ input?: Record<string, any>
1211
+ }
1212
+ ```
1213
+
1214
+ **ClientToolCall** (interface)
1215
+ ```typescript
1216
+ interface ClientToolCall {
1217
+ callId: string
1218
+ name: string
1219
+ args: Record<string, any>
1220
+ }
1221
+ ```
1222
+
1223
+ **RequiresActionResult** (interface)
1224
+ ```typescript
1225
+ interface RequiresActionResult {
1226
+ status: 'requires_action'
1227
+ client_tool_calls: ClientToolCall[]
1228
+ items: any[]
1229
+ steps?: number
1230
+ toolResults?: AgentToolResult[]
1231
+ availableTools?: string[]
1232
+ }
1233
+ ```
1234
+
1235
+ **ClientTool** (interface)
1236
+ ```typescript
1237
+ interface ClientTool {
1238
+ declaration: ClientToolDeclaration
1239
+ handler: (args: Record<string, any>) => any | Promise<any>
1240
+ }
1241
+ ```
1242
+
1243
+ **RunWithClientToolsOptions** (interface)
1244
+ ```typescript
1245
+ interface RunWithClientToolsOptions {
1246
+ input: string
1247
+ tools: ClientTool[]
1248
+ surface?: 'admin' | 'public'
1249
+ instructions?: string
1250
+ model?: string
1251
+ maxSteps?: number
1252
+ toolbelt?: AgentToolbelt
1253
+ maxRounds?: number
1254
+ }
1255
+ ```
1256
+
1257
+ **PublicAgentRunRequest** (interface)
1258
+ ```typescript
1259
+ interface PublicAgentRunRequest {
1260
+ input?: string
1261
+ prompt?: string
1262
+ instructions?: string
1263
+ model?: string
1264
+ maxSteps?: number
1265
+ server_tools?: AiToolName[]
1266
+ only?: AiToolName[]
1267
+ exclude?: AiToolName[]
1268
+ allowCapabilities?: AiToolCapability[]
1269
+ appFunctions?: AgentAppFunctionsOption
1270
+ toolbelt?: AgentToolbelt
1271
+ userId?: string
1180
1272
  }
1181
1273
  ```
1182
1274
 
@@ -1189,6 +1281,15 @@ interface AgentToolResult {
1189
1281
  }
1190
1282
  ```
1191
1283
 
1284
+ **ToolRunResult<T = any>** (interface)
1285
+ ```typescript
1286
+ interface ToolRunResult<T = any> {
1287
+ name: string
1288
+ isError: boolean
1289
+ result: T
1290
+ }
1291
+ ```
1292
+
1192
1293
  **AgentRunResult** (interface)
1193
1294
  ```typescript
1194
1295
  interface AgentRunResult {
@@ -1367,6 +1468,41 @@ interface PdfMergeArgs {
1367
1468
  }
1368
1469
  ```
1369
1470
 
1471
+ **PdfInspectArgs** (interface)
1472
+ ```typescript
1473
+ interface PdfInspectArgs {
1474
+ url: string; maxPages?: number; minTextChars?: number
1475
+ }
1476
+ ```
1477
+
1478
+ **PdfRenderArgs** (interface)
1479
+ ```typescript
1480
+ interface PdfRenderArgs {
1481
+ url: string; page?: number; dpi?: number
1482
+ }
1483
+ ```
1484
+
1485
+ **PdfExtractArgs** (interface)
1486
+ ```typescript
1487
+ interface PdfExtractArgs {
1488
+ url: string; schema?: Record<string, any>; prompt?: string; maxPages?: number; dpi?: number; includeConfidence?: boolean; includeBoxes?: boolean
1489
+ }
1490
+ ```
1491
+
1492
+ **PdfDecodeBarcodesArgs** (interface)
1493
+ ```typescript
1494
+ interface PdfDecodeBarcodesArgs {
1495
+ url: string; page?: number; dpi?: number
1496
+ }
1497
+ ```
1498
+
1499
+ **PdfInspectGraphicsArgs** (interface)
1500
+ ```typescript
1501
+ interface PdfInspectGraphicsArgs {
1502
+ url: string; page?: number
1503
+ }
1504
+ ```
1505
+
1370
1506
  **HttpRequestArgs** (interface)
1371
1507
  ```typescript
1372
1508
  interface HttpRequestArgs {
@@ -1399,6 +1535,11 @@ interface AiToolArgsMap {
1399
1535
  'pdf.create': PdfCreateArgs
1400
1536
  'pdf.fill': PdfFillArgs
1401
1537
  'pdf.merge': PdfMergeArgs
1538
+ 'pdf.inspect': PdfInspectArgs
1539
+ 'pdf.render': PdfRenderArgs
1540
+ 'pdf.extract': PdfExtractArgs
1541
+ 'pdf.decodeBarcodes': PdfDecodeBarcodesArgs
1542
+ 'pdf.inspectGraphics': PdfInspectGraphicsArgs
1402
1543
  'http.request': HttpRequestArgs
1403
1544
  'translate': TranslateArgs
1404
1545
  }
@@ -1467,6 +1608,69 @@ interface TranslateResult {
1467
1608
  }
1468
1609
  ```
1469
1610
 
1611
+ **PdfInspectPage** (interface)
1612
+ ```typescript
1613
+ interface PdfInspectPage {
1614
+ page: number; width: number; height: number; textChars: number; hasText: boolean; imageCount: number; likelyType: 'text-native' | 'text+raster' | 'raster-only' | 'curve-only'
1615
+ }
1616
+ ```
1617
+
1618
+ **PdfInspectResult** (interface)
1619
+ ```typescript
1620
+ interface PdfInspectResult {
1621
+ url: string; pageCount: number; inspectedPages: number; totalTextChars: number; isTextNative: boolean; isCurveOnly: boolean; recommendedPath: 'text' | 'vision'; note: string; pages: PdfInspectPage[]
1622
+ }
1623
+ ```
1624
+
1625
+ **PdfRenderResult** (interface)
1626
+ ```typescript
1627
+ interface PdfRenderResult {
1628
+ url: string | null; page: number; pageCount: number; dpi: number; width: number; height: number
1629
+ }
1630
+ ```
1631
+
1632
+ **PdfFieldMeta** (interface)
1633
+ ```typescript
1634
+ interface PdfFieldMeta {
1635
+ confidence?: number | null; source?: 'text' | 'vision' | 'inferred'; bbox?: { x: number; y: number; w: number; h: number; page: number }
1636
+ }
1637
+ ```
1638
+
1639
+ **PdfExtractResult** (interface)
1640
+ ```typescript
1641
+ interface PdfExtractResult {
1642
+ url: string; method: 'text' | 'vision'; pagesRead: number; fields: Record<string, any>; fieldsMeta?: Record<string, PdfFieldMeta>
1643
+ }
1644
+ ```
1645
+
1646
+ **PdfBarcode** (interface)
1647
+ ```typescript
1648
+ interface PdfBarcode {
1649
+ type: string; format: string; value: string; page: number; bbox?: { x: number; y: number; w: number; h: number }; confidence: number
1650
+ }
1651
+ ```
1652
+
1653
+ **PdfDecodeBarcodesResult** (interface)
1654
+ ```typescript
1655
+ interface PdfDecodeBarcodesResult {
1656
+ url: string; pagesScanned: number; codes: PdfBarcode[]
1657
+ }
1658
+ ```
1659
+
1660
+ **PdfGraphicsPage** (interface)
1661
+ ```typescript
1662
+ interface PdfGraphicsPage {
1663
+ page: number; paths: number; images: number; textRuns: number; colourSpaces: string[]
1664
+ }
1665
+ ```
1666
+
1667
+ **PdfInspectGraphicsResult** (interface)
1668
+ ```typescript
1669
+ interface PdfInspectGraphicsResult {
1670
+ url: string; pages: PdfGraphicsPage[]; spotColours: string[]; note: string
1671
+ }
1672
+ ```
1673
+
1470
1674
  **ResponsesAgentTrace** (interface)
1471
1675
  ```typescript
1472
1676
  interface ResponsesAgentTrace {
@@ -2135,6 +2339,18 @@ interface AppFunctionDef {
2135
2339
  dataScope?: 'collection' | 'global';
2136
2340
  apiVersion?: string;
2137
2341
  handler?: string;
2342
+ agent?: AppFunctionAgentExposure;
2343
+ }
2344
+ ```
2345
+
2346
+ **AppFunctionAgentExposure** (interface)
2347
+ ```typescript
2348
+ interface AppFunctionAgentExposure {
2349
+ tool: boolean;
2350
+ title?: string;
2351
+ description?: string;
2352
+ input?: Record<string, any>;
2353
+ approval?: 'auto' | 'require';
2138
2354
  }
2139
2355
  ```
2140
2356
 
@@ -11250,6 +11466,10 @@ Get the active transfer/status for a proof (owner, collection admin, or the name
11250
11466
  request: PublicChatRequest) → `Promise<PublicChatResponse>`
11251
11467
  Chat with product assistant (RAG)
11252
11468
 
11469
+ **agentRun**(collectionId: string,
11470
+ body: PublicAgentRunRequest) → `Promise<AgentRunResult>`
11471
+ Public agent loop — run the orchestration-neutral tool loop on the consumer surface. Exposes an app's PUBLIC server functions (`agent.tool:true`, `visibility:'public'`) to a consumer assistant; built-in tools are opt-in by explicit `server_tools[]` allowlist only. The caller runs as the signed-in consumer ('owner', send the authKit bearer) or anonymous ('public'). POST /public/collection/:collectionId/ai/agent/run
11472
+
11253
11473
  **getSession**(collectionId: string, sessionId: string) → `Promise<Session>`
11254
11474
  Get session history
11255
11475
 
@@ -11483,6 +11703,22 @@ Reverse lookup by ref via POST (public). `POST /public/collection/:collectionId/
11483
11703
  **renderSource**(collectionId: string,
11484
11704
  body: TemplateRenderSourceRequest) → `Promise<TemplateRenderSourceResponse>`
11485
11705
 
11706
+ ### tools
11707
+
11708
+ **run**(collectionId: string,
11709
+ name: K,
11710
+ args: AiToolArgsMap[K],) → `Promise<ToolRunResult>
11711
+ export async function run<T = any>(
11712
+ collectionId: string,
11713
+ name: string,
11714
+ args?: Record<string, any>,
11715
+ ): Promise<ToolRunResult<T>>
11716
+ export async function run(
11717
+ collectionId: string,
11718
+ name: string,
11719
+ args: Record<string, any> =`
11720
+ Invoke ONE built-in server tool directly — no model in the loop. This is the "direct code" caller of the orchestration-neutral tool registry: the SAME tools the agent loop and the Responses `server_tools` path run, but called as a plain, typed, deterministic API. A front end can use it two ways: (1) call a tool straight as an API (e.g. `pdf.render` / `pdf.extract` behind a PDF UX), or (2) drive its OWN agent loop and execute each model tool-call here. Capability-gated server-side to the caller's grants (same blast-radius rules as the agent loop). POST /admin/collection/:collectionId/ai/tools/:name/run
11721
+
11486
11722
  ### translations
11487
11723
 
11488
11724
  **hashText**(text: string, options?: TranslationHashOptions) → `Promise<string>`
@@ -69,6 +69,34 @@ The platform derives an **MCP tool list** from your manifest and drives the stan
69
69
  → `tools/call` → structured result — with streaming, cancellation, and approval layered on. You don't
70
70
  implement the wire protocol; you declare tools and write handlers. (The live loop is the staged part.)
71
71
 
72
+ ## Client tools (front-end)
73
+
74
+ For genuinely UI-coupled actions — read a field the user is editing, open a picker, update on-page
75
+ state — declare a **client tool**: the model calls it, but it runs in your page, not on the server.
76
+ Build each with `ai.defineClientTool` and drive the loop with `ai.runWithClientTools`, which resolves
77
+ every client call automatically (suspend → run handler → resubmit) until the assistant answers:
78
+
79
+ ```ts
80
+ const pickDate = SL.ai.defineClientTool(
81
+ 'ui.pickDate',
82
+ { description: 'Open the date picker and return the chosen ISO date.',
83
+ input: { type: 'object', properties: { min: { type: 'string' } } } },
84
+ async ({ min }) => ({ date: await openDatePicker({ min }) }) // runs in the browser
85
+ )
86
+
87
+ const result = await SL.ai.runWithClientTools(collectionId, {
88
+ input: 'Book me the earliest slot next week',
89
+ tools: [pickDate],
90
+ toolbelt: { builtins: ['web.search'], appFunctions: [{ appId: 'booking' }] }, // mix all three kinds
91
+ // surface: 'public' // for a consumer assistant
92
+ })
93
+ ```
94
+
95
+ The handler runs with the user's own session (auto-scoped), and only tools you declared can be called
96
+ (a call for anything else throws). A client-tool name can never collide with a built-in or a server
97
+ function — those always win. This is the third tool **kind**, alongside built-in tools and app
98
+ functions, all declared together in one `toolbelt`.
99
+
72
100
  ## Security
73
101
 
74
102
  Identical to server functions — nothing new to reason about:
package/dist/docs/ai.md CHANGED
@@ -324,15 +324,40 @@ in `input`.
324
324
  | `image.searchStock` | Search real stock photos (Unsplash). |
325
325
  | `image.transform` | Resize / crop / rotate / grayscale / format-convert / compress → hosted URL. |
326
326
  | `pdf.create` | Render HTML → PDF → hosted URL. |
327
- | `pdf.fill` | Fill an AcroForm PDF's fields → hosted URL. |
328
- | `pdf.merge` | Merge several PDFs into one → hosted URL. |
327
+ | `pdf.fill` | Fill an AcroForm PDF's fields (`{ field: value }`) → hosted URL. |
328
+ | `pdf.merge` | Merge several PDFs into one, in order → hosted URL. |
329
+ | `pdf.inspect` | Cheap, no-AI introspection: page count/sizes, which pages have a real text layer, raster present, and a routing hint (`text` vs `vision`). |
330
+ | `pdf.render` | Rasterize one page to a PNG at a chosen DPI → hosted image URL (feed to vision, or screenshot a page). |
331
+ | `pdf.extract` | PDF → typed JSON in one call (schema and/or prompt). Auto-routes text vs vision. Optional per-field `confidence`/`source` (`includeConfidence`) and `bbox` (`includeBoxes`) in `fieldsMeta`. |
332
+ | `pdf.decodeBarcodes` | Deterministically decode barcodes/QR on a page (WASM, no AI) → value + symbology + page + bbox + confidence. Use this for barcode digits, never vision. |
333
+ | `pdf.inspectGraphics` | Prepress inspection: per-page path/image/outlined-text counts + colour spaces, plus named SPOT colours (e.g. "PANTONE 871 C"). No AI. |
329
334
  | `http.request` | SSRF-guarded outbound HTTP(S) to a public URL (call a REST API). |
330
335
  | `translate` | Translate text into one or more languages (generic, model-based). |
331
336
 
337
+ **Working with PDFs.** The PDF tools split into *read/analyse* and *produce*:
338
+
339
+ - **Read a whole PDF as text** → `document.read` (Firecrawl; good for prose/decks).
340
+ - **Turn a PDF into structured fields** → `pdf.extract` (give it a JSON schema and/or a prompt; it
341
+ returns typed JSON). It routes itself, but you can drive the route yourself: call `pdf.inspect`
342
+ first (deterministic, no AI) to see whether each page has a real text layer, then `pdf.extract`
343
+ (cheap text path) or `pdf.render` → `image.describe`/vision for curve-only or raster artwork.
344
+ - **Zoom in on small print** (INCI/allergen lists) → `pdf.render` at a high DPI (e.g. 300), then read
345
+ the PNG with vision.
346
+ - **Barcodes / QR** → `pdf.decodeBarcodes` (deterministic WASM decode) — never trust vision for barcode
347
+ digits; it hallucinates them.
348
+ - **Prepress / print QA** (spot colours, colour spaces, vector vs raster) → `pdf.inspectGraphics`.
349
+ - **A review UI that flags guessed fields** → `pdf.extract` with `includeConfidence` (per-field
350
+ confidence + source) and `includeBoxes` (per-field bbox on text-native pages) in `fieldsMeta`.
351
+ - **Produce a PDF** → `pdf.create` (HTML → PDF), `pdf.fill` (populate an AcroForm's fields),
352
+ `pdf.merge` (combine several). These return a hosted `hostedUrl`.
353
+
354
+ Every tool is also directly callable without the model loop via `ai.tools.run(collectionId, name,
355
+ args)` — e.g. render a page or extract fields straight from a UI, no agent round-trip.
356
+
332
357
  Discover tools two ways:
333
358
  - **Design time (typed):** import `BUILTIN_AI_TOOLS`, `AI_TOOL_NAMES`, and the per-tool arg types
334
- (`WebSearchArgs`, `DataExtractArgs`, …) from the SDK. This is the core set — stable, versioned,
335
- documented here.
359
+ (`WebSearchArgs`, `DataExtractArgs`, `PdfExtractArgs`, …) from the SDK. This is the core set —
360
+ stable, versioned, documented here.
336
361
  - **Runtime (live):** `await ai.catalog(collectionId)` returns the registry as the server sees it,
337
362
  including any future app-contributed tools. The built-in set above is always present.
338
363