libretto 0.2.3 → 0.2.5

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.
Files changed (42) hide show
  1. package/README.md +78 -184
  2. package/dist/cli/cli.js +8 -2
  3. package/dist/cli/commands/browser.js +26 -3
  4. package/dist/cli/commands/execution.js +50 -11
  5. package/dist/cli/commands/init.js +95 -0
  6. package/dist/cli/core/browser.js +131 -6
  7. package/dist/cli/core/context.js +5 -0
  8. package/dist/cli/core/session.js +13 -13
  9. package/dist/cli/workers/run-integration-runtime.js +64 -59
  10. package/dist/cli/workers/run-integration-worker-protocol.js +12 -0
  11. package/dist/cli/workers/run-integration-worker.js +13 -30
  12. package/dist/index.cjs +5 -12
  13. package/dist/index.d.cts +4 -2
  14. package/dist/index.d.ts +4 -2
  15. package/dist/index.js +5 -15
  16. package/dist/shared/debug/index.cjs +4 -6
  17. package/dist/shared/debug/index.d.cts +1 -2
  18. package/dist/shared/debug/index.d.ts +1 -2
  19. package/dist/shared/debug/index.js +3 -8
  20. package/dist/shared/debug/pause.cjs +58 -24
  21. package/dist/shared/debug/pause.d.cts +13 -20
  22. package/dist/shared/debug/pause.d.ts +13 -20
  23. package/dist/shared/debug/pause.js +46 -21
  24. package/dist/shared/llm/ai-sdk-adapter.cjs +67 -0
  25. package/dist/shared/llm/ai-sdk-adapter.d.cts +22 -0
  26. package/dist/shared/llm/ai-sdk-adapter.d.ts +22 -0
  27. package/dist/shared/llm/ai-sdk-adapter.js +43 -0
  28. package/dist/shared/llm/index.cjs +5 -2
  29. package/dist/shared/llm/index.d.cts +2 -0
  30. package/dist/shared/llm/index.d.ts +2 -0
  31. package/dist/shared/llm/index.js +3 -1
  32. package/dist/shared/llm/types.d.cts +32 -0
  33. package/dist/shared/llm/types.d.ts +32 -0
  34. package/dist/shared/run/api.cjs +0 -7
  35. package/dist/shared/run/api.d.cts +0 -1
  36. package/dist/shared/run/api.d.ts +0 -1
  37. package/dist/shared/run/api.js +0 -8
  38. package/dist/shared/workflow/workflow.d.cts +11 -24
  39. package/dist/shared/workflow/workflow.d.ts +11 -24
  40. package/package.json +4 -10
  41. package/skill/SKILL.md +18 -5
  42. package/skill/code-generation-rules.md +7 -10
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  prettyConsoleSink,
5
5
  jsonlConsoleSink
6
6
  } from "./shared/logger/sinks.js";
7
+ import { createLLMClientFromModel } from "./shared/llm/ai-sdk-adapter.js";
7
8
  import {
8
9
  SESSION_STATE_VERSION,
9
10
  SessionStatusSchema,
@@ -25,11 +26,7 @@ import {
25
26
  downloadViaClick,
26
27
  downloadAndSave
27
28
  } from "./runtime/download/download.js";
28
- import {
29
- debugPause,
30
- DebugPauseSignal,
31
- isDebugPauseSignal
32
- } from "./shared/debug/pause.js";
29
+ import { pause } from "./shared/debug/pause.js";
33
30
  import {
34
31
  isDebugMode,
35
32
  isDryRun,
@@ -52,10 +49,7 @@ import {
52
49
  clearHighlights
53
50
  } from "./shared/visualization/highlight.js";
54
51
  import {
55
- launchBrowser,
56
- debugPause as debugPause2,
57
- DebugPauseSignal as DebugPauseSignal2,
58
- isDebugPauseSignal as isDebugPauseSignal2
52
+ launchBrowser
59
53
  } from "./shared/run/api.js";
60
54
  import {
61
55
  LibrettoWorkflow,
@@ -63,18 +57,16 @@ import {
63
57
  workflow
64
58
  } from "./shared/workflow/workflow.js";
65
59
  export {
66
- DebugPauseSignal,
67
60
  LIBRETTO_WORKFLOW_BRAND,
68
61
  LibrettoWorkflow,
69
62
  Logger,
70
- DebugPauseSignal2 as RunDebugPauseSignal,
71
63
  SESSION_STATE_VERSION,
72
64
  SessionStateFileSchema,
73
65
  SessionStatusSchema,
74
66
  attemptWithRecovery,
75
67
  clearHighlights,
76
68
  createFileLogSink,
77
- debugPause,
69
+ createLLMClientFromModel,
78
70
  defaultLogger,
79
71
  detectSubmissionError,
80
72
  downloadAndSave,
@@ -89,17 +81,15 @@ export {
89
81
  instrumentContext,
90
82
  instrumentPage,
91
83
  isDebugMode,
92
- isDebugPauseSignal,
93
84
  isDryRun,
94
- isDebugPauseSignal2 as isRunDebugPauseSignal,
95
85
  jsonlConsoleSink,
96
86
  launchBrowser,
97
87
  moveGhostCursor,
98
88
  pageRequest,
99
89
  parseSessionStateContent,
100
90
  parseSessionStateData,
91
+ pause,
101
92
  prettyConsoleSink,
102
- debugPause2 as runDebugPause,
103
93
  serializeSessionState,
104
94
  shouldPauseBeforeMutation,
105
95
  showHighlight,
@@ -18,15 +18,13 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var debug_exports = {};
20
20
  __export(debug_exports, {
21
- DebugPauseSignal: () => import_pause.DebugPauseSignal,
22
- debugPause: () => import_pause.debugPause,
23
- isDebugPauseSignal: () => import_pause.isDebugPauseSignal
21
+ pause: () => import_pause.pause,
22
+ setSessionForPause: () => import_pause.setSessionForPause
24
23
  });
25
24
  module.exports = __toCommonJS(debug_exports);
26
25
  var import_pause = require("./pause.js");
27
26
  // Annotate the CommonJS export names for ESM import in node:
28
27
  0 && (module.exports = {
29
- DebugPauseSignal,
30
- debugPause,
31
- isDebugPauseSignal
28
+ pause,
29
+ setSessionForPause
32
30
  });
@@ -1,2 +1 @@
1
- export { DebugPauseContext, DebugPauseDetails, DebugPauseSignal, debugPause, isDebugPauseSignal } from './pause.cjs';
2
- import 'playwright';
1
+ export { pause, setSessionForPause } from './pause.cjs';
@@ -1,2 +1 @@
1
- export { DebugPauseContext, DebugPauseDetails, DebugPauseSignal, debugPause, isDebugPauseSignal } from './pause.js';
2
- import 'playwright';
1
+ export { pause, setSessionForPause } from './pause.js';
@@ -1,10 +1,5 @@
1
- import {
2
- debugPause,
3
- DebugPauseSignal,
4
- isDebugPauseSignal
5
- } from "./pause.js";
1
+ import { pause, setSessionForPause } from "./pause.js";
6
2
  export {
7
- DebugPauseSignal,
8
- debugPause,
9
- isDebugPauseSignal
3
+ pause,
4
+ setSessionForPause
10
5
  };
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,42 +17,74 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var pause_exports = {};
20
30
  __export(pause_exports, {
21
- DebugPauseSignal: () => DebugPauseSignal,
22
- debugPause: () => debugPause,
23
- isDebugPauseSignal: () => isDebugPauseSignal
31
+ pause: () => pause,
32
+ setSessionForPause: () => setSessionForPause
24
33
  });
25
34
  module.exports = __toCommonJS(pause_exports);
26
- class DebugPauseSignal extends Error {
27
- details;
28
- constructor(details) {
29
- super(`Workflow paused at ${details.url}`);
30
- this.name = "DebugPauseSignal";
31
- this.details = details;
35
+ var import_node_fs = require("node:fs");
36
+ var import_promises = require("node:fs/promises");
37
+ let _sessionName;
38
+ function setSessionForPause(session) {
39
+ _sessionName = session;
40
+ }
41
+ function getSessionFromProcessArgs() {
42
+ const rawPayload = process.argv[2];
43
+ if (!rawPayload) return void 0;
44
+ try {
45
+ const parsed = JSON.parse(rawPayload);
46
+ return typeof parsed.session === "string" ? parsed.session : void 0;
47
+ } catch {
48
+ return void 0;
32
49
  }
33
50
  }
34
- function isDebugPauseSignal(error) {
35
- if (!error || typeof error !== "object") return false;
36
- const candidate = error;
37
- if (candidate.name !== "DebugPauseSignal") return false;
38
- return typeof candidate.details?.sessionName === "string" && typeof candidate.details?.pausedAt === "string" && typeof candidate.details?.url === "string";
51
+ function resolveSession() {
52
+ return _sessionName ?? getSessionFromProcessArgs();
39
53
  }
40
- async function debugPause(context) {
41
- const url = context.page.url();
54
+ async function pause() {
55
+ if (process.env.NODE_ENV === "production") {
56
+ return;
57
+ }
58
+ const session = resolveSession();
59
+ if (!session) {
60
+ return;
61
+ }
62
+ const { getPauseSignalPaths, removeSignalIfExists } = await import("../../cli/core/pause-signals.js");
63
+ const { getSessionDir } = await import("../../cli/core/context.js");
64
+ const signalPaths = getPauseSignalPaths(session);
65
+ const { pausedSignalPath, resumeSignalPath } = signalPaths;
66
+ await (0, import_promises.mkdir)(getSessionDir(session), { recursive: true });
67
+ await removeSignalIfExists(resumeSignalPath);
42
68
  const details = {
43
- sessionName: context.session,
69
+ sessionName: session,
44
70
  pausedAt: (/* @__PURE__ */ new Date()).toISOString(),
45
- url
71
+ url: "unknown"
46
72
  };
47
- console.log(`[debugPause] Paused at ${url}`);
48
- console.log("[debugPause] Signaling pause to supervisor...");
49
- throw new DebugPauseSignal(details);
73
+ await (0, import_promises.writeFile)(pausedSignalPath, JSON.stringify(details, null, 2), "utf8");
74
+ console.log(`[pause] Paused (session: ${session})`);
75
+ console.log("[pause] Waiting for resume signal...");
76
+ const RESUME_POLL_INTERVAL_MS = 250;
77
+ while (!(0, import_node_fs.existsSync)(resumeSignalPath)) {
78
+ await new Promise(
79
+ (resolve) => setTimeout(resolve, RESUME_POLL_INTERVAL_MS)
80
+ );
81
+ }
82
+ await removeSignalIfExists(resumeSignalPath);
83
+ await removeSignalIfExists(pausedSignalPath);
84
+ console.log("[pause] Resume signal received. Continuing workflow...");
50
85
  }
51
86
  // Annotate the CommonJS export names for ESM import in node:
52
87
  0 && (module.exports = {
53
- DebugPauseSignal,
54
- debugPause,
55
- isDebugPauseSignal
88
+ pause,
89
+ setSessionForPause
56
90
  });
@@ -1,23 +1,16 @@
1
- import { Page } from 'playwright';
2
-
3
- type DebugPauseContext = {
4
- page: Page;
5
- session: string;
6
- };
7
- type DebugPauseDetails = {
8
- sessionName: string;
9
- pausedAt: string;
10
- url: string;
11
- };
12
- declare class DebugPauseSignal extends Error {
13
- readonly details: DebugPauseDetails;
14
- constructor(details: DebugPauseDetails);
15
- }
16
- declare function isDebugPauseSignal(error: unknown): error is DebugPauseSignal;
17
1
  /**
18
- * Signals a workflow pause to the caller.
19
- * This always throws a typed signal that supervisors can intercept.
2
+ * Called by the CLI runtime to make the session name available to `pause()`.
3
+ */
4
+ declare function setSessionForPause(session: string): void;
5
+ /**
6
+ * Standalone pause function.
7
+ *
8
+ * - In production (`NODE_ENV === "production"`), returns immediately (no-op).
9
+ * - Otherwise, writes a `.paused` signal file and polls for a `.resume` signal,
10
+ * using the same file-based mechanism as the CLI runner.
11
+ *
12
+ * Import directly: `import { pause } from "libretto";`
20
13
  */
21
- declare function debugPause(context: DebugPauseContext): Promise<never>;
14
+ declare function pause(): Promise<void>;
22
15
 
23
- export { type DebugPauseContext, type DebugPauseDetails, DebugPauseSignal, debugPause, isDebugPauseSignal };
16
+ export { pause, setSessionForPause };
@@ -1,23 +1,16 @@
1
- import { Page } from 'playwright';
2
-
3
- type DebugPauseContext = {
4
- page: Page;
5
- session: string;
6
- };
7
- type DebugPauseDetails = {
8
- sessionName: string;
9
- pausedAt: string;
10
- url: string;
11
- };
12
- declare class DebugPauseSignal extends Error {
13
- readonly details: DebugPauseDetails;
14
- constructor(details: DebugPauseDetails);
15
- }
16
- declare function isDebugPauseSignal(error: unknown): error is DebugPauseSignal;
17
1
  /**
18
- * Signals a workflow pause to the caller.
19
- * This always throws a typed signal that supervisors can intercept.
2
+ * Called by the CLI runtime to make the session name available to `pause()`.
3
+ */
4
+ declare function setSessionForPause(session: string): void;
5
+ /**
6
+ * Standalone pause function.
7
+ *
8
+ * - In production (`NODE_ENV === "production"`), returns immediately (no-op).
9
+ * - Otherwise, writes a `.paused` signal file and polls for a `.resume` signal,
10
+ * using the same file-based mechanism as the CLI runner.
11
+ *
12
+ * Import directly: `import { pause } from "libretto";`
20
13
  */
21
- declare function debugPause(context: DebugPauseContext): Promise<never>;
14
+ declare function pause(): Promise<void>;
22
15
 
23
- export { type DebugPauseContext, type DebugPauseDetails, DebugPauseSignal, debugPause, isDebugPauseSignal };
16
+ export { pause, setSessionForPause };
@@ -1,30 +1,55 @@
1
- class DebugPauseSignal extends Error {
2
- details;
3
- constructor(details) {
4
- super(`Workflow paused at ${details.url}`);
5
- this.name = "DebugPauseSignal";
6
- this.details = details;
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ let _sessionName;
4
+ function setSessionForPause(session) {
5
+ _sessionName = session;
6
+ }
7
+ function getSessionFromProcessArgs() {
8
+ const rawPayload = process.argv[2];
9
+ if (!rawPayload) return void 0;
10
+ try {
11
+ const parsed = JSON.parse(rawPayload);
12
+ return typeof parsed.session === "string" ? parsed.session : void 0;
13
+ } catch {
14
+ return void 0;
7
15
  }
8
16
  }
9
- function isDebugPauseSignal(error) {
10
- if (!error || typeof error !== "object") return false;
11
- const candidate = error;
12
- if (candidate.name !== "DebugPauseSignal") return false;
13
- return typeof candidate.details?.sessionName === "string" && typeof candidate.details?.pausedAt === "string" && typeof candidate.details?.url === "string";
17
+ function resolveSession() {
18
+ return _sessionName ?? getSessionFromProcessArgs();
14
19
  }
15
- async function debugPause(context) {
16
- const url = context.page.url();
20
+ async function pause() {
21
+ if (process.env.NODE_ENV === "production") {
22
+ return;
23
+ }
24
+ const session = resolveSession();
25
+ if (!session) {
26
+ return;
27
+ }
28
+ const { getPauseSignalPaths, removeSignalIfExists } = await import("../../cli/core/pause-signals.js");
29
+ const { getSessionDir } = await import("../../cli/core/context.js");
30
+ const signalPaths = getPauseSignalPaths(session);
31
+ const { pausedSignalPath, resumeSignalPath } = signalPaths;
32
+ await mkdir(getSessionDir(session), { recursive: true });
33
+ await removeSignalIfExists(resumeSignalPath);
17
34
  const details = {
18
- sessionName: context.session,
35
+ sessionName: session,
19
36
  pausedAt: (/* @__PURE__ */ new Date()).toISOString(),
20
- url
37
+ url: "unknown"
21
38
  };
22
- console.log(`[debugPause] Paused at ${url}`);
23
- console.log("[debugPause] Signaling pause to supervisor...");
24
- throw new DebugPauseSignal(details);
39
+ await writeFile(pausedSignalPath, JSON.stringify(details, null, 2), "utf8");
40
+ console.log(`[pause] Paused (session: ${session})`);
41
+ console.log("[pause] Waiting for resume signal...");
42
+ const RESUME_POLL_INTERVAL_MS = 250;
43
+ while (!existsSync(resumeSignalPath)) {
44
+ await new Promise(
45
+ (resolve) => setTimeout(resolve, RESUME_POLL_INTERVAL_MS)
46
+ );
47
+ }
48
+ await removeSignalIfExists(resumeSignalPath);
49
+ await removeSignalIfExists(pausedSignalPath);
50
+ console.log("[pause] Resume signal received. Continuing workflow...");
25
51
  }
26
52
  export {
27
- DebugPauseSignal,
28
- debugPause,
29
- isDebugPauseSignal
53
+ pause,
54
+ setSessionForPause
30
55
  };
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var ai_sdk_adapter_exports = {};
20
+ __export(ai_sdk_adapter_exports, {
21
+ createLLMClientFromModel: () => createLLMClientFromModel
22
+ });
23
+ module.exports = __toCommonJS(ai_sdk_adapter_exports);
24
+ var import_ai = require("ai");
25
+ function createLLMClientFromModel(model) {
26
+ return {
27
+ async generateObject(opts) {
28
+ const { object } = await (0, import_ai.generateObject)({
29
+ model,
30
+ schema: opts.schema,
31
+ prompt: opts.prompt,
32
+ temperature: opts.temperature ?? 0
33
+ });
34
+ return object;
35
+ },
36
+ async generateObjectFromMessages(opts) {
37
+ const messages = opts.messages.map((msg) => {
38
+ if (typeof msg.content === "string") {
39
+ return { role: msg.role, content: msg.content };
40
+ }
41
+ if (msg.role === "assistant") {
42
+ return {
43
+ role: "assistant",
44
+ content: msg.content.filter((part) => part.type === "text").map((part) => ({ type: "text", text: part.text }))
45
+ };
46
+ }
47
+ return {
48
+ role: "user",
49
+ content: msg.content.map(
50
+ (part) => part.type === "text" ? { type: "text", text: part.text } : { type: "image", image: part.image }
51
+ )
52
+ };
53
+ });
54
+ const { object } = await (0, import_ai.generateObject)({
55
+ model,
56
+ schema: opts.schema,
57
+ messages,
58
+ temperature: opts.temperature ?? 0
59
+ });
60
+ return object;
61
+ }
62
+ };
63
+ }
64
+ // Annotate the CommonJS export names for ESM import in node:
65
+ 0 && (module.exports = {
66
+ createLLMClientFromModel
67
+ });
@@ -0,0 +1,22 @@
1
+ import { LanguageModel } from 'ai';
2
+ import { LLMClient } from './types.cjs';
3
+ import 'zod';
4
+
5
+ /**
6
+ * Creates a libretto LLMClient from a Vercel AI SDK LanguageModel.
7
+ *
8
+ * This eliminates the need for consumers to write their own adapter
9
+ * when using @ai-sdk/openai, @ai-sdk/anthropic, @ai-sdk/google-vertex,
10
+ * or any other Vercel AI SDK-compatible provider.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { createLLMClientFromModel } from "libretto/llm";
15
+ * import { openai } from "@ai-sdk/openai";
16
+ *
17
+ * const llmClient = createLLMClientFromModel(openai("gpt-4o"));
18
+ * ```
19
+ */
20
+ declare function createLLMClientFromModel(model: LanguageModel): LLMClient;
21
+
22
+ export { createLLMClientFromModel };
@@ -0,0 +1,22 @@
1
+ import { LanguageModel } from 'ai';
2
+ import { LLMClient } from './types.js';
3
+ import 'zod';
4
+
5
+ /**
6
+ * Creates a libretto LLMClient from a Vercel AI SDK LanguageModel.
7
+ *
8
+ * This eliminates the need for consumers to write their own adapter
9
+ * when using @ai-sdk/openai, @ai-sdk/anthropic, @ai-sdk/google-vertex,
10
+ * or any other Vercel AI SDK-compatible provider.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { createLLMClientFromModel } from "libretto/llm";
15
+ * import { openai } from "@ai-sdk/openai";
16
+ *
17
+ * const llmClient = createLLMClientFromModel(openai("gpt-4o"));
18
+ * ```
19
+ */
20
+ declare function createLLMClientFromModel(model: LanguageModel): LLMClient;
21
+
22
+ export { createLLMClientFromModel };
@@ -0,0 +1,43 @@
1
+ import { generateObject } from "ai";
2
+ function createLLMClientFromModel(model) {
3
+ return {
4
+ async generateObject(opts) {
5
+ const { object } = await generateObject({
6
+ model,
7
+ schema: opts.schema,
8
+ prompt: opts.prompt,
9
+ temperature: opts.temperature ?? 0
10
+ });
11
+ return object;
12
+ },
13
+ async generateObjectFromMessages(opts) {
14
+ const messages = opts.messages.map((msg) => {
15
+ if (typeof msg.content === "string") {
16
+ return { role: msg.role, content: msg.content };
17
+ }
18
+ if (msg.role === "assistant") {
19
+ return {
20
+ role: "assistant",
21
+ content: msg.content.filter((part) => part.type === "text").map((part) => ({ type: "text", text: part.text }))
22
+ };
23
+ }
24
+ return {
25
+ role: "user",
26
+ content: msg.content.map(
27
+ (part) => part.type === "text" ? { type: "text", text: part.text } : { type: "image", image: part.image }
28
+ )
29
+ };
30
+ });
31
+ const { object } = await generateObject({
32
+ model,
33
+ schema: opts.schema,
34
+ messages,
35
+ temperature: opts.temperature ?? 0
36
+ });
37
+ return object;
38
+ }
39
+ };
40
+ }
41
+ export {
42
+ createLLMClientFromModel
43
+ };
@@ -18,11 +18,14 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var llm_exports = {};
20
20
  __export(llm_exports, {
21
- createLLMClient: () => import_client.createLLMClient
21
+ createLLMClient: () => import_client.createLLMClient,
22
+ createLLMClientFromModel: () => import_ai_sdk_adapter.createLLMClientFromModel
22
23
  });
23
24
  module.exports = __toCommonJS(llm_exports);
24
25
  var import_client = require("./client.js");
26
+ var import_ai_sdk_adapter = require("./ai-sdk-adapter.js");
25
27
  // Annotate the CommonJS export names for ESM import in node:
26
28
  0 && (module.exports = {
27
- createLLMClient
29
+ createLLMClient,
30
+ createLLMClientFromModel
28
31
  });
@@ -1,3 +1,5 @@
1
1
  export { LLMClient, Message, MessageContentPart } from './types.cjs';
2
2
  export { createLLMClient } from './client.cjs';
3
+ export { createLLMClientFromModel } from './ai-sdk-adapter.cjs';
3
4
  import 'zod';
5
+ import 'ai';
@@ -1,3 +1,5 @@
1
1
  export { LLMClient, Message, MessageContentPart } from './types.js';
2
2
  export { createLLMClient } from './client.js';
3
+ export { createLLMClientFromModel } from './ai-sdk-adapter.js';
3
4
  import 'zod';
5
+ import 'ai';
@@ -1,4 +1,6 @@
1
1
  import { createLLMClient } from "./client.js";
2
+ import { createLLMClientFromModel } from "./ai-sdk-adapter.js";
2
3
  export {
3
- createLLMClient
4
+ createLLMClient,
5
+ createLLMClientFromModel
4
6
  };
@@ -17,13 +17,45 @@ type Message = {
17
17
  * Users provide their own implementation backed by any LLM provider
18
18
  * (OpenAI, Anthropic, etc.). Libretto uses this interface for AI extraction,
19
19
  * recovery agents, and error detection.
20
+ *
21
+ * **Error handling:** implementations should throw on failure rather than
22
+ * returning sentinel values (e.g. `null` or `undefined`). Libretto relies
23
+ * on exceptions to trigger retry/recovery logic.
24
+ *
25
+ * A ready-made adapter for the Vercel AI SDK is available via
26
+ * {@link createLLMClientFromModel} in `libretto/llm`.
20
27
  */
21
28
  interface LLMClient {
29
+ /**
30
+ * Generate a structured object from a single text prompt.
31
+ *
32
+ * The underlying model **must** support structured / JSON output so that
33
+ * the response can be parsed and validated against the provided Zod schema.
34
+ *
35
+ * @param opts.prompt - The text prompt sent to the model.
36
+ * @param opts.schema - A Zod schema describing the expected response shape.
37
+ * @param opts.temperature - Sampling temperature (default chosen by implementation, typically 0).
38
+ * @returns The parsed object matching the schema.
39
+ * @throws On LLM or parsing failure.
40
+ */
22
41
  generateObject<T extends z.ZodType>(opts: {
23
42
  prompt: string;
24
43
  schema: T;
25
44
  temperature?: number;
26
45
  }): Promise<z.infer<T>>;
46
+ /**
47
+ * Generate a structured object from a conversation-style message array.
48
+ *
49
+ * Messages may contain **image content** (base64 data URIs via
50
+ * {@link MessageContentPart}), so the backing model must support
51
+ * vision / multimodal input when images are present.
52
+ *
53
+ * @param opts.messages - Ordered list of user/assistant messages, potentially multimodal.
54
+ * @param opts.schema - A Zod schema describing the expected response shape.
55
+ * @param opts.temperature - Sampling temperature (default chosen by implementation, typically 0).
56
+ * @returns The parsed object matching the schema.
57
+ * @throws On LLM or parsing failure.
58
+ */
27
59
  generateObjectFromMessages<T extends z.ZodType>(opts: {
28
60
  messages: Message[];
29
61
  schema: T;
@@ -17,13 +17,45 @@ type Message = {
17
17
  * Users provide their own implementation backed by any LLM provider
18
18
  * (OpenAI, Anthropic, etc.). Libretto uses this interface for AI extraction,
19
19
  * recovery agents, and error detection.
20
+ *
21
+ * **Error handling:** implementations should throw on failure rather than
22
+ * returning sentinel values (e.g. `null` or `undefined`). Libretto relies
23
+ * on exceptions to trigger retry/recovery logic.
24
+ *
25
+ * A ready-made adapter for the Vercel AI SDK is available via
26
+ * {@link createLLMClientFromModel} in `libretto/llm`.
20
27
  */
21
28
  interface LLMClient {
29
+ /**
30
+ * Generate a structured object from a single text prompt.
31
+ *
32
+ * The underlying model **must** support structured / JSON output so that
33
+ * the response can be parsed and validated against the provided Zod schema.
34
+ *
35
+ * @param opts.prompt - The text prompt sent to the model.
36
+ * @param opts.schema - A Zod schema describing the expected response shape.
37
+ * @param opts.temperature - Sampling temperature (default chosen by implementation, typically 0).
38
+ * @returns The parsed object matching the schema.
39
+ * @throws On LLM or parsing failure.
40
+ */
22
41
  generateObject<T extends z.ZodType>(opts: {
23
42
  prompt: string;
24
43
  schema: T;
25
44
  temperature?: number;
26
45
  }): Promise<z.infer<T>>;
46
+ /**
47
+ * Generate a structured object from a conversation-style message array.
48
+ *
49
+ * Messages may contain **image content** (base64 data URIs via
50
+ * {@link MessageContentPart}), so the backing model must support
51
+ * vision / multimodal input when images are present.
52
+ *
53
+ * @param opts.messages - Ordered list of user/assistant messages, potentially multimodal.
54
+ * @param opts.schema - A Zod schema describing the expected response shape.
55
+ * @param opts.temperature - Sampling temperature (default chosen by implementation, typically 0).
56
+ * @returns The parsed object matching the schema.
57
+ * @throws On LLM or parsing failure.
58
+ */
27
59
  generateObjectFromMessages<T extends z.ZodType>(opts: {
28
60
  messages: Message[];
29
61
  schema: T;