markform 0.1.21 → 0.1.23
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/README.md +66 -18
- package/dist/ai-sdk.d.mts +1 -1
- package/dist/ai-sdk.mjs +1 -1
- package/dist/{apply-CD-t7ovb.mjs → apply-KzQztrDV.mjs} +100 -74
- package/dist/apply-KzQztrDV.mjs.map +1 -0
- package/dist/bin.mjs +1 -1
- package/dist/{cli-ChdIy1a7.mjs → cli-ZcOC47KK.mjs} +24 -1213
- package/dist/cli-ZcOC47KK.mjs.map +1 -0
- package/dist/cli.mjs +1 -1
- package/dist/{coreTypes-BQrWf_Wt.d.mts → coreTypes-BlsJkU1w.d.mts} +1 -1
- package/dist/fillRecord-DTl5lnK0.d.mts +345 -0
- package/dist/fillRecordRenderer-VBQ2vwPV.mjs +1253 -0
- package/dist/fillRecordRenderer-VBQ2vwPV.mjs.map +1 -0
- package/dist/index.d.mts +53 -343
- package/dist/index.mjs +4 -4
- package/dist/render.d.mts +74 -0
- package/dist/render.mjs +4 -0
- package/dist/{session-ZgegwtkT.mjs → session-BCcltrLA.mjs} +1 -1
- package/dist/{session-ZgegwtkT.mjs.map → session-BCcltrLA.mjs.map} +1 -1
- package/dist/{session-BPuQ-ok0.mjs → session-VeSkVrck.mjs} +1 -1
- package/dist/{shared-DwdyWmvE.mjs → shared-CsdT2T7k.mjs} +1 -1
- package/dist/{shared-DwdyWmvE.mjs.map → shared-CsdT2T7k.mjs.map} +1 -1
- package/dist/{shared-BTR35aMz.mjs → shared-fb0nkzQi.mjs} +1 -1
- package/dist/{src-DOPe4tmu.mjs → src-B2uFvGli.mjs} +103 -21
- package/dist/{src-DOPe4tmu.mjs.map → src-B2uFvGli.mjs.map} +1 -1
- package/dist/urlFormat-lls7CsEP.mjs +71 -0
- package/dist/urlFormat-lls7CsEP.mjs.map +1 -0
- package/docs/markform-apis.md +53 -0
- package/examples/simple/simple-skipped-filled.report.md +8 -8
- package/examples/twitter-thread/twitter-thread.form.md +373 -0
- package/package.json +5 -1
- package/dist/apply-CD-t7ovb.mjs.map +0 -1
- package/dist/cli-ChdIy1a7.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-
|
|
1
|
+
{"version":3,"file":"session-BCcltrLA.mjs","names":[],"sources":["../src/engine/session.ts"],"sourcesContent":["/**\n * Session module - parsing and serializing session transcripts.\n *\n * Session transcripts are used for golden testing and session replay.\n * They capture the full interaction between the harness and agent.\n */\nimport YAML from 'yaml';\nimport type { SessionTranscript } from './coreTypes';\nimport { SessionTranscriptSchema } from './coreTypes';\n\n/**\n * Parse a session transcript from YAML string.\n *\n * Converts snake_case keys to camelCase for TypeScript consumption.\n *\n * @param yaml - YAML string containing session transcript\n * @returns Parsed and validated SessionTranscript\n * @throws Error if YAML is invalid or doesn't match schema\n */\nexport function parseSession(yaml: string): SessionTranscript {\n // Parse YAML\n let raw: unknown;\n try {\n raw = YAML.parse(yaml);\n } catch (err) {\n throw new Error(\n `Failed to parse session YAML: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // Convert snake_case to camelCase\n const converted = toCamelCaseDeep(raw);\n\n // Validate against schema\n const result = SessionTranscriptSchema.safeParse(converted);\n if (!result.success) {\n const errors = result.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join('; ');\n throw new Error(`Invalid session transcript: ${errors}`);\n }\n\n return result.data;\n}\n\n/**\n * Serialize a session transcript to YAML string.\n *\n * Converts camelCase keys to snake_case for YAML output.\n *\n * @param session - Session transcript to serialize\n * @returns YAML string\n */\nexport function serializeSession(session: SessionTranscript): string {\n // Convert camelCase to snake_case\n const snakeCased = toSnakeCaseDeep(session);\n\n // Serialize to YAML\n return YAML.stringify(snakeCased, {\n indent: 2,\n lineWidth: 0, // Don't wrap lines\n });\n}\n\n// =============================================================================\n// Key Case Conversion Helpers\n// =============================================================================\n\n/**\n * Convert a string from snake_case to camelCase.\n */\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_match, letter: string) => letter.toUpperCase());\n}\n\n/**\n * Convert a string from camelCase to snake_case.\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * Recursively convert all object keys from snake_case to camelCase.\n *\n * Preserves keys that are user-defined identifiers (like option IDs in\n * checkboxes `values` objects).\n *\n * @param obj - Object to convert\n * @param preserveKeys - If true, don't convert keys in this object (but still recurse into values)\n */\nfunction toCamelCaseDeep(obj: unknown, preserveKeys = false): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n // Pass preserveKeys through to array items\n return obj.map((item) => toCamelCaseDeep(item, preserveKeys));\n }\n\n if (typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n const record = obj as Record<string, unknown>;\n\n for (const [key, value] of Object.entries(record)) {\n // Determine the key to use\n const resultKey = preserveKeys ? key : snakeToCamel(key);\n\n // Check if this is a \"value\" key in a set_checkboxes patch\n // The \"value\" object contains option IDs as keys which should be preserved\n const isCheckboxValues = key === 'value' && record.op === 'set_checkboxes';\n\n // Check if this is a \"value\" key in a set_table patch\n // The \"value\" array contains objects with column IDs as keys which should be preserved\n const isTableRows = key === 'value' && record.op === 'set_table';\n\n // Check if this is a \"tools\" key in a wire format request\n // Tool names are identifiers that should be preserved\n const isWireTools = key === 'tools';\n\n result[resultKey] = toCamelCaseDeep(value, isCheckboxValues || isTableRows || isWireTools);\n }\n return result;\n }\n\n return obj;\n}\n\n/**\n * Recursively convert all object keys from camelCase to snake_case.\n *\n * Preserves keys that are user-defined identifiers (like option IDs in\n * checkboxes `values` objects).\n *\n * @param obj - Object to convert\n * @param preserveKeys - If true, don't convert keys in this object (but still recurse into values)\n */\nfunction toSnakeCaseDeep(obj: unknown, preserveKeys = false): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n // Pass preserveKeys through to array items\n return obj.map((item) => toSnakeCaseDeep(item, preserveKeys));\n }\n\n if (typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n const record = obj as Record<string, unknown>;\n\n for (const [key, value] of Object.entries(record)) {\n // Determine the key to use\n const resultKey = preserveKeys ? key : camelToSnake(key);\n\n // Check if this is a \"value\" key in a set_checkboxes patch\n // The \"value\" object contains option IDs as keys which should be preserved\n const isCheckboxValues = key === 'value' && record.op === 'set_checkboxes';\n\n // Check if this is a \"value\" key in a set_table patch\n // The \"value\" array contains objects with column IDs as keys which should be preserved\n const isTableRows = key === 'value' && record.op === 'set_table';\n\n // Check if this is a \"tools\" key in a wire format request\n // Tool names are identifiers that should be preserved\n const isWireTools = key === 'tools';\n\n result[resultKey] = toSnakeCaseDeep(value, isCheckboxValues || isTableRows || isWireTools);\n }\n return result;\n }\n\n return obj;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,aAAa,MAAiC;CAE5D,IAAI;AACJ,KAAI;AACF,QAAM,KAAK,MAAM,KAAK;UACf,KAAK;AACZ,QAAM,IAAI,MACR,iCAAiC,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAClF;;CAIH,MAAM,YAAY,gBAAgB,IAAI;CAGtC,MAAM,SAAS,wBAAwB,UAAU,UAAU;AAC3D,KAAI,CAAC,OAAO,SAAS;EACnB,MAAM,SAAS,OAAO,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,KAAK;AAC7F,QAAM,IAAI,MAAM,+BAA+B,SAAS;;AAG1D,QAAO,OAAO;;;;;;;;;;AAWhB,SAAgB,iBAAiB,SAAoC;CAEnE,MAAM,aAAa,gBAAgB,QAAQ;AAG3C,QAAO,KAAK,UAAU,YAAY;EAChC,QAAQ;EACR,WAAW;EACZ,CAAC;;;;;AAUJ,SAAS,aAAa,KAAqB;AACzC,QAAO,IAAI,QAAQ,cAAc,QAAQ,WAAmB,OAAO,aAAa,CAAC;;;;;AAMnF,SAAS,aAAa,KAAqB;AACzC,QAAO,IAAI,QAAQ,WAAW,WAAW,IAAI,OAAO,aAAa,GAAG;;;;;;;;;;;AAYtE,SAAS,gBAAgB,KAAc,eAAe,OAAgB;AACpE,KAAI,QAAQ,QAAQ,QAAQ,OAC1B,QAAO;AAGT,KAAI,MAAM,QAAQ,IAAI,CAEpB,QAAO,IAAI,KAAK,SAAS,gBAAgB,MAAM,aAAa,CAAC;AAG/D,KAAI,OAAO,QAAQ,UAAU;EAC3B,MAAM,SAAkC,EAAE;EAC1C,MAAM,SAAS;AAEf,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAAE;GAEjD,MAAM,YAAY,eAAe,MAAM,aAAa,IAAI;GAIxD,MAAM,mBAAmB,QAAQ,WAAW,OAAO,OAAO;GAI1D,MAAM,cAAc,QAAQ,WAAW,OAAO,OAAO;AAMrD,UAAO,aAAa,gBAAgB,OAAO,oBAAoB,eAF3C,QAAQ,QAE8D;;AAE5F,SAAO;;AAGT,QAAO;;;;;;;;;;;AAYT,SAAS,gBAAgB,KAAc,eAAe,OAAgB;AACpE,KAAI,QAAQ,QAAQ,QAAQ,OAC1B,QAAO;AAGT,KAAI,MAAM,QAAQ,IAAI,CAEpB,QAAO,IAAI,KAAK,SAAS,gBAAgB,MAAM,aAAa,CAAC;AAG/D,KAAI,OAAO,QAAQ,UAAU;EAC3B,MAAM,SAAkC,EAAE;EAC1C,MAAM,SAAS;AAEf,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAAE;GAEjD,MAAM,YAAY,eAAe,MAAM,aAAa,IAAI;GAIxD,MAAM,mBAAmB,QAAQ,WAAW,OAAO,OAAO;GAI1D,MAAM,cAAc,QAAQ,WAAW,OAAO,OAAO;AAMrD,UAAO,aAAa,gBAAgB,OAAO,oBAAoB,eAF3C,QAAQ,QAE8D;;AAE5F,SAAO;;AAGT,QAAO"}
|
|
@@ -262,4 +262,4 @@ async function ensureFormsDir(formsDir) {
|
|
|
262
262
|
|
|
263
263
|
//#endregion
|
|
264
264
|
export { writeFile as _, formatPath as a, logError as c, logTiming as d, logVerbose as f, stripHtmlComments as g, shouldUseColors as h, formatOutput as i, logInfo as l, readFile$1 as m, createSpinner as n, getCommandContext as o, logWarn as p, ensureFormsDir as r, logDryRun as s, OUTPUT_FORMATS as t, logSuccess as u };
|
|
265
|
-
//# sourceMappingURL=shared-
|
|
265
|
+
//# sourceMappingURL=shared-CsdT2T7k.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-DwdyWmvE.mjs","names":["readFile"],"sources":["../src/cli/lib/naming.ts","../src/cli/lib/shared.ts"],"sourcesContent":["/**\n * Naming convention utilities for JSON/YAML output.\n *\n * Converts between camelCase (TypeScript internal) and snake_case (JSON/YAML output).\n */\n\n/**\n * Convert a camelCase string to snake_case.\n *\n * @example\n * toSnakeCase(\"fieldCount\") // \"field_count\"\n * toSnakeCase(\"parentFieldId\") // \"parent_field_id\"\n * toSnakeCase(\"already_snake\") // \"already_snake\"\n */\nexport function toSnakeCase(str: string): string {\n return str.replace(/([A-Z])/g, '_$1').toLowerCase();\n}\n\n/**\n * Convert a snake_case string to camelCase.\n *\n * @example\n * toCamelCase(\"field_count\") // \"fieldCount\"\n * toCamelCase(\"parent_field_id\") // \"parentFieldId\"\n * toCamelCase(\"alreadyCamel\") // \"alreadyCamel\"\n */\nexport function toCamelCase(str: string): string {\n return str.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase());\n}\n\n/**\n * Recursively convert all object keys from camelCase to snake_case.\n *\n * Handles nested objects and arrays. Primitives are returned unchanged.\n */\nexport function convertKeysToSnakeCase(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(convertKeysToSnakeCase);\n }\n\n if (typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n const snakeKey = toSnakeCase(key);\n result[snakeKey] = convertKeysToSnakeCase(value);\n }\n return result;\n }\n\n return obj;\n}\n\n/**\n * Recursively convert all object keys from snake_case to camelCase.\n *\n * Handles nested objects and arrays. Primitives are returned unchanged.\n */\nexport function convertKeysToCamelCase(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(convertKeysToCamelCase);\n }\n\n if (typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n const camelKey = toCamelCase(key);\n result[camelKey] = convertKeysToCamelCase(value);\n }\n return result;\n }\n\n return obj;\n}\n","/**\n * Shared CLI utilities for command context, debug, and dry-run helpers.\n */\n\nimport type { Command } from 'commander';\n\nimport { mkdir } from 'node:fs/promises';\nimport { relative } from 'node:path';\n\nimport * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport YAML from 'yaml';\n\nimport { convertKeysToSnakeCase } from './naming.js';\nimport type { CommandContext, OutputFormat } from './cliTypes.js';\n\n// =============================================================================\n// Spinner Utility Types\n// =============================================================================\n\n/**\n * Context type for spinner operations.\n * - 'api': For LLM/API calls (shows provider, model, turn info)\n * - 'compute': For local calculations\n */\nexport type SpinnerContextType = 'api' | 'compute';\n\n/**\n * API context for spinner - used when making LLM calls.\n */\nexport interface ApiSpinnerContext {\n type: 'api';\n provider: string;\n model: string;\n turnNumber?: number;\n}\n\n/**\n * Compute context for spinner - used for local calculations.\n */\nexport interface ComputeSpinnerContext {\n type: 'compute';\n operation: string;\n}\n\n/**\n * Union of spinner context types.\n */\nexport type SpinnerContext = ApiSpinnerContext | ComputeSpinnerContext;\n\n/**\n * Handle for controlling an active spinner.\n */\nexport interface SpinnerHandle {\n /** Update the spinner message. */\n message(msg: string): void;\n /** Update the spinner context (re-renders with elapsed time). */\n update(context: SpinnerContext): void;\n /** Stop the spinner with a success message. */\n stop(msg?: string): void;\n /** Stop the spinner with an error message. */\n error(msg: string): void;\n /** Get elapsed time in milliseconds since spinner started. */\n getElapsedMs(): number;\n}\n\n// Re-export types for backwards compatibility\nexport type { CommandContext, OutputFormat } from './cliTypes.js';\n\n// =============================================================================\n// Spinner Utility Functions\n// =============================================================================\n\n/**\n * Format elapsed time for display.\n */\nfunction formatElapsedTime(ms: number): string {\n const seconds = ms / 1000;\n if (seconds < 60) {\n return `${seconds.toFixed(1)}s`;\n }\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes}m ${remainingSeconds.toFixed(0)}s`;\n}\n\n/**\n * Format spinner message based on context type.\n */\nfunction formatSpinnerMessage(context: SpinnerContext, elapsedMs: number): string {\n const elapsed = formatElapsedTime(elapsedMs);\n\n if (context.type === 'api') {\n const turnInfo = context.turnNumber !== undefined ? ` turn ${context.turnNumber}` : '';\n return `${context.provider}/${context.model}${turnInfo} ${pc.dim(`(${elapsed})`)}`;\n }\n\n return `${context.operation} ${pc.dim(`(${elapsed})`)}`;\n}\n\n/**\n * Create a context-aware spinner with elapsed time tracking.\n *\n * The spinner automatically updates its message with elapsed time.\n *\n * @example\n * ```ts\n * const spinner = createSpinner({\n * type: 'api',\n * provider: 'anthropic',\n * model: 'claude-sonnet-4',\n * turnNumber: 1,\n * });\n *\n * // Do async work...\n * const result = await agent.fillFormTool(...);\n *\n * spinner.stop('Done');\n * ```\n */\nexport function createSpinner(context: SpinnerContext): SpinnerHandle {\n const startTime = Date.now();\n const spinner = p.spinner();\n let currentContext = context;\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n // Start the spinner with initial message\n const initialMessage = formatSpinnerMessage(currentContext, 0);\n spinner.start(initialMessage);\n\n // Update elapsed time every second\n intervalId = setInterval(() => {\n const elapsed = Date.now() - startTime;\n spinner.message(formatSpinnerMessage(currentContext, elapsed));\n }, 1000);\n\n const cleanup = (): void => {\n if (intervalId) {\n clearInterval(intervalId);\n intervalId = null;\n }\n };\n\n return {\n message(msg: string): void {\n spinner.message(msg);\n },\n\n update(newContext: SpinnerContext): void {\n currentContext = newContext;\n const elapsed = Date.now() - startTime;\n spinner.message(formatSpinnerMessage(currentContext, elapsed));\n },\n\n stop(msg?: string): void {\n cleanup();\n const elapsed = Date.now() - startTime;\n const defaultMsg = formatSpinnerMessage(currentContext, elapsed);\n spinner.stop(msg ?? `✓ ${defaultMsg}`);\n },\n\n error(msg: string): void {\n cleanup();\n spinner.stop(pc.red(`✗ ${msg}`));\n },\n\n getElapsedMs(): number {\n return Date.now() - startTime;\n },\n };\n}\n\n/**\n * Create a no-op spinner handle for quiet mode or non-TTY environments.\n */\nexport function createNoOpSpinner(): SpinnerHandle {\n const startTime = Date.now();\n // Use explicit undefined returns to avoid empty-function lint errors\n const noop = (): void => undefined;\n return {\n message: noop,\n update: noop,\n stop: noop,\n error: noop,\n getElapsedMs: () => Date.now() - startTime,\n };\n}\n\n/**\n * Create a spinner if appropriate for the context.\n * Returns a no-op spinner in quiet mode or when stdout is not a TTY.\n */\nexport function createSpinnerIfTty(context: SpinnerContext, ctx: CommandContext): SpinnerHandle {\n if (ctx.quiet || !process.stdout.isTTY) {\n return createNoOpSpinner();\n }\n return createSpinner(context);\n}\n\n// =============================================================================\n// Output Format Utilities\n// =============================================================================\n\n/**\n * Valid format options for Commander choice validation.\n */\nexport const OUTPUT_FORMATS: OutputFormat[] = [\n 'console',\n 'plaintext',\n 'yaml',\n 'json',\n 'markform',\n 'markdown',\n];\n\n/**\n * Extract command context from Commander options.\n */\nexport function getCommandContext(command: Command): CommandContext {\n const opts = command.optsWithGlobals<{\n dryRun?: boolean;\n verbose?: boolean;\n quiet?: boolean;\n format?: OutputFormat;\n formsDir?: string;\n overwrite?: boolean;\n }>();\n return {\n dryRun: opts.dryRun ?? false,\n verbose: opts.verbose ?? false,\n quiet: opts.quiet ?? false,\n format: opts.format ?? 'console',\n formsDir: opts.formsDir,\n overwrite: opts.overwrite ?? false,\n };\n}\n\n/**\n * Check if output should use colors.\n * Returns true for console format when stdout is a TTY.\n */\nexport function shouldUseColors(ctx: CommandContext): boolean {\n if (ctx.format === 'plaintext' || ctx.format === 'yaml' || ctx.format === 'json') {\n return false;\n }\n // console format: use colors if stdout is a TTY and NO_COLOR is not set\n return process.stdout.isTTY && !process.env.NO_COLOR;\n}\n\n/**\n * Format structured data according to output format.\n *\n * JSON and YAML outputs are converted to snake_case keys for consistency.\n */\nexport function formatOutput(\n ctx: CommandContext,\n data: unknown,\n consoleFormatter?: (data: unknown, useColors: boolean) => string,\n): string {\n switch (ctx.format) {\n case 'json':\n return JSON.stringify(convertKeysToSnakeCase(data), null, 2);\n case 'yaml':\n return YAML.stringify(convertKeysToSnakeCase(data));\n case 'plaintext':\n case 'console':\n default:\n if (consoleFormatter) {\n return consoleFormatter(data, shouldUseColors(ctx));\n }\n // Default: use YAML for readable console output\n return YAML.stringify(convertKeysToSnakeCase(data));\n }\n}\n\n/**\n * Log a dry-run message.\n */\nexport function logDryRun(message: string, details?: unknown): void {\n console.log(pc.yellow(`[DRY RUN] ${message}`));\n if (details) {\n console.log(pc.dim(JSON.stringify(details, null, 2)));\n }\n}\n\n/**\n * Log a verbose message (only shown if --verbose is set).\n */\nexport function logVerbose(ctx: CommandContext, message: string): void {\n if (ctx.verbose) {\n console.log(pc.dim(message));\n }\n}\n\n/**\n * Log an info message (hidden if --quiet is set).\n */\nexport function logInfo(ctx: CommandContext, message: string): void {\n if (!ctx.quiet) {\n console.log(message);\n }\n}\n\n/**\n * Log an error message (always shown).\n */\nexport function logError(message: string): void {\n console.error(pc.red(`Error: ${message}`));\n}\n\n/**\n * Log a success message (hidden if --quiet is set).\n */\nexport function logSuccess(ctx: CommandContext, message: string): void {\n if (!ctx.quiet) {\n console.log(pc.green(message));\n }\n}\n\n/**\n * Log a timing message (hidden if --quiet is set).\n */\nexport function logTiming(ctx: CommandContext, label: string, durationMs: number): void {\n if (!ctx.quiet) {\n const seconds = (durationMs / 1000).toFixed(1);\n console.log(pc.cyan(`⏰ ${label}: ${seconds}s`));\n }\n}\n\n/**\n * Log a warning message (hidden if --quiet is set).\n */\nexport function logWarn(ctx: CommandContext, message: string): void {\n if (!ctx.quiet) {\n console.log(pc.yellow(`⚠️ ${message}`));\n }\n}\n\n/**\n * Strip HTML comments from markdown content.\n * Removes <!-- ... --> blocks (including multiline) and trims leading whitespace.\n */\nexport function stripHtmlComments(content: string): string {\n // Remove HTML comments (multiline-safe)\n const stripped = content.replace(/<!--[\\s\\S]*?-->/g, '');\n // Trim leading whitespace that may remain after comment removal\n return stripped.replace(/^\\s+/, '');\n}\n\n/**\n * Format a file path for display: relative to cwd, colored green.\n * If the path is within the cwd, shows as relative (e.g., \"./simple-filled1.form.md\")\n * If outside cwd, shows the absolute path.\n */\nexport function formatPath(absolutePath: string, cwd: string = process.cwd()): string {\n const relativePath = relative(cwd, absolutePath);\n // If the relative path doesn't start with \"..\", it's within cwd\n const displayPath = relativePath.startsWith('..') ? absolutePath : `./${relativePath}`;\n return pc.green(displayPath);\n}\n\n/**\n * Read a file and return its contents.\n */\nexport async function readFile(filePath: string): Promise<string> {\n const { readFile: fsReadFile } = await import('node:fs/promises');\n return fsReadFile(filePath, 'utf-8');\n}\n\n/**\n * Write contents to a file atomically.\n *\n * Uses the atomically library to prevent partial or corrupted files\n * if the process crashes mid-write.\n */\nexport async function writeFile(filePath: string, contents: string): Promise<void> {\n const { writeFile: atomicWriteFile } = await import('atomically');\n await atomicWriteFile(filePath, contents);\n}\n\n/**\n * Ensure the forms directory exists, creating it if necessary.\n * Uses recursive mkdir so parent directories are created as needed.\n *\n * @param formsDir Absolute path to the forms directory\n */\nexport async function ensureFormsDir(formsDir: string): Promise<void> {\n await mkdir(formsDir, { recursive: true });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shared-CsdT2T7k.mjs","names":["readFile"],"sources":["../src/cli/lib/naming.ts","../src/cli/lib/shared.ts"],"sourcesContent":["/**\n * Naming convention utilities for JSON/YAML output.\n *\n * Converts between camelCase (TypeScript internal) and snake_case (JSON/YAML output).\n */\n\n/**\n * Convert a camelCase string to snake_case.\n *\n * @example\n * toSnakeCase(\"fieldCount\") // \"field_count\"\n * toSnakeCase(\"parentFieldId\") // \"parent_field_id\"\n * toSnakeCase(\"already_snake\") // \"already_snake\"\n */\nexport function toSnakeCase(str: string): string {\n return str.replace(/([A-Z])/g, '_$1').toLowerCase();\n}\n\n/**\n * Convert a snake_case string to camelCase.\n *\n * @example\n * toCamelCase(\"field_count\") // \"fieldCount\"\n * toCamelCase(\"parent_field_id\") // \"parentFieldId\"\n * toCamelCase(\"alreadyCamel\") // \"alreadyCamel\"\n */\nexport function toCamelCase(str: string): string {\n return str.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase());\n}\n\n/**\n * Recursively convert all object keys from camelCase to snake_case.\n *\n * Handles nested objects and arrays. Primitives are returned unchanged.\n */\nexport function convertKeysToSnakeCase(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(convertKeysToSnakeCase);\n }\n\n if (typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n const snakeKey = toSnakeCase(key);\n result[snakeKey] = convertKeysToSnakeCase(value);\n }\n return result;\n }\n\n return obj;\n}\n\n/**\n * Recursively convert all object keys from snake_case to camelCase.\n *\n * Handles nested objects and arrays. Primitives are returned unchanged.\n */\nexport function convertKeysToCamelCase(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(convertKeysToCamelCase);\n }\n\n if (typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n const camelKey = toCamelCase(key);\n result[camelKey] = convertKeysToCamelCase(value);\n }\n return result;\n }\n\n return obj;\n}\n","/**\n * Shared CLI utilities for command context, debug, and dry-run helpers.\n */\n\nimport type { Command } from 'commander';\n\nimport { mkdir } from 'node:fs/promises';\nimport { relative } from 'node:path';\n\nimport * as p from '@clack/prompts';\nimport pc from 'picocolors';\nimport YAML from 'yaml';\n\nimport { convertKeysToSnakeCase } from './naming.js';\nimport type { CommandContext, OutputFormat } from './cliTypes.js';\n\n// =============================================================================\n// Spinner Utility Types\n// =============================================================================\n\n/**\n * Context type for spinner operations.\n * - 'api': For LLM/API calls (shows provider, model, turn info)\n * - 'compute': For local calculations\n */\nexport type SpinnerContextType = 'api' | 'compute';\n\n/**\n * API context for spinner - used when making LLM calls.\n */\nexport interface ApiSpinnerContext {\n type: 'api';\n provider: string;\n model: string;\n turnNumber?: number;\n}\n\n/**\n * Compute context for spinner - used for local calculations.\n */\nexport interface ComputeSpinnerContext {\n type: 'compute';\n operation: string;\n}\n\n/**\n * Union of spinner context types.\n */\nexport type SpinnerContext = ApiSpinnerContext | ComputeSpinnerContext;\n\n/**\n * Handle for controlling an active spinner.\n */\nexport interface SpinnerHandle {\n /** Update the spinner message. */\n message(msg: string): void;\n /** Update the spinner context (re-renders with elapsed time). */\n update(context: SpinnerContext): void;\n /** Stop the spinner with a success message. */\n stop(msg?: string): void;\n /** Stop the spinner with an error message. */\n error(msg: string): void;\n /** Get elapsed time in milliseconds since spinner started. */\n getElapsedMs(): number;\n}\n\n// Re-export types for backwards compatibility\nexport type { CommandContext, OutputFormat } from './cliTypes.js';\n\n// =============================================================================\n// Spinner Utility Functions\n// =============================================================================\n\n/**\n * Format elapsed time for display.\n */\nfunction formatElapsedTime(ms: number): string {\n const seconds = ms / 1000;\n if (seconds < 60) {\n return `${seconds.toFixed(1)}s`;\n }\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes}m ${remainingSeconds.toFixed(0)}s`;\n}\n\n/**\n * Format spinner message based on context type.\n */\nfunction formatSpinnerMessage(context: SpinnerContext, elapsedMs: number): string {\n const elapsed = formatElapsedTime(elapsedMs);\n\n if (context.type === 'api') {\n const turnInfo = context.turnNumber !== undefined ? ` turn ${context.turnNumber}` : '';\n return `${context.provider}/${context.model}${turnInfo} ${pc.dim(`(${elapsed})`)}`;\n }\n\n return `${context.operation} ${pc.dim(`(${elapsed})`)}`;\n}\n\n/**\n * Create a context-aware spinner with elapsed time tracking.\n *\n * The spinner automatically updates its message with elapsed time.\n *\n * @example\n * ```ts\n * const spinner = createSpinner({\n * type: 'api',\n * provider: 'anthropic',\n * model: 'claude-sonnet-4',\n * turnNumber: 1,\n * });\n *\n * // Do async work...\n * const result = await agent.fillFormTool(...);\n *\n * spinner.stop('Done');\n * ```\n */\nexport function createSpinner(context: SpinnerContext): SpinnerHandle {\n const startTime = Date.now();\n const spinner = p.spinner();\n let currentContext = context;\n let intervalId: ReturnType<typeof setInterval> | null = null;\n\n // Start the spinner with initial message\n const initialMessage = formatSpinnerMessage(currentContext, 0);\n spinner.start(initialMessage);\n\n // Update elapsed time every second\n intervalId = setInterval(() => {\n const elapsed = Date.now() - startTime;\n spinner.message(formatSpinnerMessage(currentContext, elapsed));\n }, 1000);\n\n const cleanup = (): void => {\n if (intervalId) {\n clearInterval(intervalId);\n intervalId = null;\n }\n };\n\n return {\n message(msg: string): void {\n spinner.message(msg);\n },\n\n update(newContext: SpinnerContext): void {\n currentContext = newContext;\n const elapsed = Date.now() - startTime;\n spinner.message(formatSpinnerMessage(currentContext, elapsed));\n },\n\n stop(msg?: string): void {\n cleanup();\n const elapsed = Date.now() - startTime;\n const defaultMsg = formatSpinnerMessage(currentContext, elapsed);\n spinner.stop(msg ?? `✓ ${defaultMsg}`);\n },\n\n error(msg: string): void {\n cleanup();\n spinner.stop(pc.red(`✗ ${msg}`));\n },\n\n getElapsedMs(): number {\n return Date.now() - startTime;\n },\n };\n}\n\n/**\n * Create a no-op spinner handle for quiet mode or non-TTY environments.\n */\nexport function createNoOpSpinner(): SpinnerHandle {\n const startTime = Date.now();\n // Use explicit undefined returns to avoid empty-function lint errors\n const noop = (): void => undefined;\n return {\n message: noop,\n update: noop,\n stop: noop,\n error: noop,\n getElapsedMs: () => Date.now() - startTime,\n };\n}\n\n/**\n * Create a spinner if appropriate for the context.\n * Returns a no-op spinner in quiet mode or when stdout is not a TTY.\n */\nexport function createSpinnerIfTty(context: SpinnerContext, ctx: CommandContext): SpinnerHandle {\n if (ctx.quiet || !process.stdout.isTTY) {\n return createNoOpSpinner();\n }\n return createSpinner(context);\n}\n\n// =============================================================================\n// Output Format Utilities\n// =============================================================================\n\n/**\n * Valid format options for Commander choice validation.\n */\nexport const OUTPUT_FORMATS: OutputFormat[] = [\n 'console',\n 'plaintext',\n 'yaml',\n 'json',\n 'markform',\n 'markdown',\n];\n\n/**\n * Extract command context from Commander options.\n */\nexport function getCommandContext(command: Command): CommandContext {\n const opts = command.optsWithGlobals<{\n dryRun?: boolean;\n verbose?: boolean;\n quiet?: boolean;\n format?: OutputFormat;\n formsDir?: string;\n overwrite?: boolean;\n }>();\n return {\n dryRun: opts.dryRun ?? false,\n verbose: opts.verbose ?? false,\n quiet: opts.quiet ?? false,\n format: opts.format ?? 'console',\n formsDir: opts.formsDir,\n overwrite: opts.overwrite ?? false,\n };\n}\n\n/**\n * Check if output should use colors.\n * Returns true for console format when stdout is a TTY.\n */\nexport function shouldUseColors(ctx: CommandContext): boolean {\n if (ctx.format === 'plaintext' || ctx.format === 'yaml' || ctx.format === 'json') {\n return false;\n }\n // console format: use colors if stdout is a TTY and NO_COLOR is not set\n return process.stdout.isTTY && !process.env.NO_COLOR;\n}\n\n/**\n * Format structured data according to output format.\n *\n * JSON and YAML outputs are converted to snake_case keys for consistency.\n */\nexport function formatOutput(\n ctx: CommandContext,\n data: unknown,\n consoleFormatter?: (data: unknown, useColors: boolean) => string,\n): string {\n switch (ctx.format) {\n case 'json':\n return JSON.stringify(convertKeysToSnakeCase(data), null, 2);\n case 'yaml':\n return YAML.stringify(convertKeysToSnakeCase(data));\n case 'plaintext':\n case 'console':\n default:\n if (consoleFormatter) {\n return consoleFormatter(data, shouldUseColors(ctx));\n }\n // Default: use YAML for readable console output\n return YAML.stringify(convertKeysToSnakeCase(data));\n }\n}\n\n/**\n * Log a dry-run message.\n */\nexport function logDryRun(message: string, details?: unknown): void {\n console.log(pc.yellow(`[DRY RUN] ${message}`));\n if (details) {\n console.log(pc.dim(JSON.stringify(details, null, 2)));\n }\n}\n\n/**\n * Log a verbose message (only shown if --verbose is set).\n */\nexport function logVerbose(ctx: CommandContext, message: string): void {\n if (ctx.verbose) {\n console.log(pc.dim(message));\n }\n}\n\n/**\n * Log an info message (hidden if --quiet is set).\n */\nexport function logInfo(ctx: CommandContext, message: string): void {\n if (!ctx.quiet) {\n console.log(message);\n }\n}\n\n/**\n * Log an error message (always shown).\n */\nexport function logError(message: string): void {\n console.error(pc.red(`Error: ${message}`));\n}\n\n/**\n * Log a success message (hidden if --quiet is set).\n */\nexport function logSuccess(ctx: CommandContext, message: string): void {\n if (!ctx.quiet) {\n console.log(pc.green(message));\n }\n}\n\n/**\n * Log a timing message (hidden if --quiet is set).\n */\nexport function logTiming(ctx: CommandContext, label: string, durationMs: number): void {\n if (!ctx.quiet) {\n const seconds = (durationMs / 1000).toFixed(1);\n console.log(pc.cyan(`⏰ ${label}: ${seconds}s`));\n }\n}\n\n/**\n * Log a warning message (hidden if --quiet is set).\n */\nexport function logWarn(ctx: CommandContext, message: string): void {\n if (!ctx.quiet) {\n console.log(pc.yellow(`⚠️ ${message}`));\n }\n}\n\n/**\n * Strip HTML comments from markdown content.\n * Removes <!-- ... --> blocks (including multiline) and trims leading whitespace.\n */\nexport function stripHtmlComments(content: string): string {\n // Remove HTML comments (multiline-safe)\n const stripped = content.replace(/<!--[\\s\\S]*?-->/g, '');\n // Trim leading whitespace that may remain after comment removal\n return stripped.replace(/^\\s+/, '');\n}\n\n/**\n * Format a file path for display: relative to cwd, colored green.\n * If the path is within the cwd, shows as relative (e.g., \"./simple-filled1.form.md\")\n * If outside cwd, shows the absolute path.\n */\nexport function formatPath(absolutePath: string, cwd: string = process.cwd()): string {\n const relativePath = relative(cwd, absolutePath);\n // If the relative path doesn't start with \"..\", it's within cwd\n const displayPath = relativePath.startsWith('..') ? absolutePath : `./${relativePath}`;\n return pc.green(displayPath);\n}\n\n/**\n * Read a file and return its contents.\n */\nexport async function readFile(filePath: string): Promise<string> {\n const { readFile: fsReadFile } = await import('node:fs/promises');\n return fsReadFile(filePath, 'utf-8');\n}\n\n/**\n * Write contents to a file atomically.\n *\n * Uses the atomically library to prevent partial or corrupted files\n * if the process crashes mid-write.\n */\nexport async function writeFile(filePath: string, contents: string): Promise<void> {\n const { writeFile: atomicWriteFile } = await import('atomically');\n await atomicWriteFile(filePath, contents);\n}\n\n/**\n * Ensure the forms directory exists, creating it if necessary.\n * Uses recursive mkdir so parent directories are created as needed.\n *\n * @param formsDir Absolute path to the forms directory\n */\nexport async function ensureFormsDir(formsDir: string): Promise<void> {\n await mkdir(formsDir, { recursive: true });\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
import { _ as writeFile, a as formatPath, c as logError, d as logTiming, f as logVerbose, g as stripHtmlComments, h as shouldUseColors, i as formatOutput, l as logInfo, m as readFile, n as createSpinner, o as getCommandContext, p as logWarn, r as ensureFormsDir, s as logDryRun, t as OUTPUT_FORMATS, u as logSuccess } from "./shared-
|
|
2
|
+
import { _ as writeFile, a as formatPath, c as logError, d as logTiming, f as logVerbose, g as stripHtmlComments, h as shouldUseColors, i as formatOutput, l as logInfo, m as readFile, n as createSpinner, o as getCommandContext, p as logWarn, r as ensureFormsDir, s as logDryRun, t as OUTPUT_FORMATS, u as logSuccess } from "./shared-CsdT2T7k.mjs";
|
|
3
3
|
|
|
4
4
|
export { writeFile };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { A as MarkformSectionInputSchema, R as PatchSchema, mt as StructureSummarySchema, z as ProgressCountsSchema } from "./coreTypes-CTLr-NGd.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { B as DEFAULT_ROLES, C as getNumberAttr, D as isTagNode, E as getValidateAttr, F as DEFAULT_MAX_TURNS, L as DEFAULT_PRIORITY, M as DEFAULT_MAX_PARALLEL_AGENTS, N as DEFAULT_MAX_PATCHES_PER_TURN, O as parseOptionText, P as DEFAULT_MAX_STEPS_PER_TURN, R as DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN, S as getBooleanAttr, T as getStringAttr, V as DEFAULT_ROLE_INSTRUCTIONS, Z as transformHarnessConfigToTs, _ as tryParseSentinelResponse, at as MarkformConfigError, b as extractOptionItems, bt as wrapApiError, c as computeProgressSummary, ct as MarkformParseError, d as serializeForm, h as preprocessCommentSyntax, i as inspect, j as DEFAULT_MAX_ISSUES_PER_TURN, k as AGENT_ROLE, l as computeStructureSummary, m as detectSyntaxStyle, r as getFieldsForRoles, t as applyPatches, tt as getWebSearchConfig, v as CHECKBOX_MARKERS, w as getStringArrayAttr, x as extractTableContent, y as extractFenceValue, z as DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN } from "./apply-KzQztrDV.mjs";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import Markdoc from "@markdoc/markdoc";
|
|
6
6
|
import YAML from "yaml";
|
|
@@ -9146,7 +9146,10 @@ var LiveAgent = class {
|
|
|
9146
9146
|
this.callbacks = config.callbacks;
|
|
9147
9147
|
this.executionId = config.executionId ?? "0-serial";
|
|
9148
9148
|
this.toolChoice = config.toolChoice ?? "required";
|
|
9149
|
-
if (this.enableWebSearch
|
|
9149
|
+
if (this.enableWebSearch) {
|
|
9150
|
+
if (config.providerTools) this.webSearchTools = config.providerTools;
|
|
9151
|
+
else if (this.provider) this.webSearchTools = loadWebSearchTools(this.provider);
|
|
9152
|
+
}
|
|
9150
9153
|
}
|
|
9151
9154
|
/**
|
|
9152
9155
|
* Get list of available tool names for this agent.
|
|
@@ -9193,14 +9196,19 @@ var LiveAgent = class {
|
|
|
9193
9196
|
executionId: this.executionId
|
|
9194
9197
|
});
|
|
9195
9198
|
} catch {}
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9199
|
+
let result;
|
|
9200
|
+
try {
|
|
9201
|
+
result = await generateText({
|
|
9202
|
+
model: this.model,
|
|
9203
|
+
system: systemPrompt,
|
|
9204
|
+
prompt: contextPrompt,
|
|
9205
|
+
tools,
|
|
9206
|
+
toolChoice: this.toolChoice,
|
|
9207
|
+
stopWhen: stepCountIs(this.maxStepsPerTurn)
|
|
9208
|
+
});
|
|
9209
|
+
} catch (error) {
|
|
9210
|
+
throw wrapApiError(error, this.provider ?? "unknown", modelId);
|
|
9211
|
+
}
|
|
9204
9212
|
if (this.callbacks?.onLlmCallEnd) try {
|
|
9205
9213
|
this.callbacks.onLlmCallEnd({
|
|
9206
9214
|
model: modelId,
|
|
@@ -9687,6 +9695,61 @@ const PROVIDERS = {
|
|
|
9687
9695
|
}
|
|
9688
9696
|
};
|
|
9689
9697
|
/**
|
|
9698
|
+
* Built-in providers available by default.
|
|
9699
|
+
* Exported so callers can inspect which providers are built-in.
|
|
9700
|
+
*/
|
|
9701
|
+
const BUILT_IN_PROVIDERS = Object.freeze({
|
|
9702
|
+
anthropic: "anthropic",
|
|
9703
|
+
openai: "openai",
|
|
9704
|
+
google: "google",
|
|
9705
|
+
xai: "xai",
|
|
9706
|
+
deepseek: "deepseek"
|
|
9707
|
+
});
|
|
9708
|
+
/** Known web search tool names from AI SDK providers. */
|
|
9709
|
+
const KNOWN_WEB_SEARCH_TOOLS = [
|
|
9710
|
+
"webSearch",
|
|
9711
|
+
"webSearch_20250305",
|
|
9712
|
+
"googleSearch",
|
|
9713
|
+
"webSearchPreview"
|
|
9714
|
+
];
|
|
9715
|
+
/**
|
|
9716
|
+
* Extract web search tools from an AI SDK provider callable.
|
|
9717
|
+
* Duck-types the `.tools` property looking for known tool factory names.
|
|
9718
|
+
*/
|
|
9719
|
+
function extractToolsFromProvider(provider) {
|
|
9720
|
+
const providerTools = provider.tools;
|
|
9721
|
+
if (!providerTools || typeof providerTools !== "object") return void 0;
|
|
9722
|
+
const extracted = {};
|
|
9723
|
+
for (const toolName of KNOWN_WEB_SEARCH_TOOLS) {
|
|
9724
|
+
const factory = providerTools[toolName];
|
|
9725
|
+
if (typeof factory === "function") try {
|
|
9726
|
+
const tool = factory({});
|
|
9727
|
+
if (tool) {
|
|
9728
|
+
const key = toolName === "googleSearch" ? "google_search" : "web_search";
|
|
9729
|
+
extracted[key] = tool;
|
|
9730
|
+
break;
|
|
9731
|
+
}
|
|
9732
|
+
} catch {}
|
|
9733
|
+
}
|
|
9734
|
+
return Object.keys(extracted).length > 0 ? extracted : void 0;
|
|
9735
|
+
}
|
|
9736
|
+
/**
|
|
9737
|
+
* Normalize a ProviderInput to a ProviderAdapter.
|
|
9738
|
+
* AI SDK provider callables are wrapped in an adapter shape.
|
|
9739
|
+
*/
|
|
9740
|
+
function normalizeProvider(input) {
|
|
9741
|
+
if (typeof input === "function") return {
|
|
9742
|
+
model: (id) => input(id),
|
|
9743
|
+
tools: extractToolsFromProvider(input)
|
|
9744
|
+
};
|
|
9745
|
+
if ("model" in input && typeof input.model === "function") return input;
|
|
9746
|
+
throw new MarkformConfigError("Invalid provider: must be a ProviderAdapter (with .model() method) or an AI SDK provider callable", {
|
|
9747
|
+
option: "providers",
|
|
9748
|
+
expectedType: "ProviderAdapter | callable",
|
|
9749
|
+
receivedValue: typeof input
|
|
9750
|
+
});
|
|
9751
|
+
}
|
|
9752
|
+
/**
|
|
9690
9753
|
* Parse a model ID string into provider and model components.
|
|
9691
9754
|
*
|
|
9692
9755
|
* @param modelIdString - Model ID in format `provider/model-id`
|
|
@@ -9707,12 +9770,6 @@ function parseModelId(modelIdString) {
|
|
|
9707
9770
|
expectedType: "non-empty provider and model-id",
|
|
9708
9771
|
receivedValue: modelIdString
|
|
9709
9772
|
});
|
|
9710
|
-
const supportedProviders = Object.keys(PROVIDERS);
|
|
9711
|
-
if (!supportedProviders.includes(provider)) throw new MarkformConfigError(`Unknown provider: "${provider}". Supported providers: ${supportedProviders.join(", ")}`, {
|
|
9712
|
-
option: "model",
|
|
9713
|
-
expectedType: `one of: ${supportedProviders.join(", ")}`,
|
|
9714
|
-
receivedValue: provider
|
|
9715
|
-
});
|
|
9716
9773
|
return {
|
|
9717
9774
|
provider,
|
|
9718
9775
|
modelId
|
|
@@ -9727,9 +9784,31 @@ function parseModelId(modelIdString) {
|
|
|
9727
9784
|
* @returns Resolved model with provider info
|
|
9728
9785
|
* @throws Error if provider not installed or API key missing
|
|
9729
9786
|
*/
|
|
9730
|
-
async function resolveModel(modelIdString) {
|
|
9787
|
+
async function resolveModel(modelIdString, providers) {
|
|
9731
9788
|
const { provider, modelId } = parseModelId(modelIdString);
|
|
9789
|
+
if (providers && provider in providers) {
|
|
9790
|
+
const adapter = normalizeProvider(providers[provider]);
|
|
9791
|
+
try {
|
|
9792
|
+
return {
|
|
9793
|
+
model: adapter.model(modelId),
|
|
9794
|
+
provider,
|
|
9795
|
+
modelId,
|
|
9796
|
+
tools: adapter.tools
|
|
9797
|
+
};
|
|
9798
|
+
} catch (error) {
|
|
9799
|
+
throw new MarkformConfigError(`Custom provider "${provider}" failed to resolve model "${modelId}": ${error instanceof Error ? error.message : String(error)}`, {
|
|
9800
|
+
option: "model",
|
|
9801
|
+
expectedType: "valid model ID",
|
|
9802
|
+
receivedValue: modelIdString
|
|
9803
|
+
});
|
|
9804
|
+
}
|
|
9805
|
+
}
|
|
9732
9806
|
const providerConfig = PROVIDERS[provider];
|
|
9807
|
+
if (!providerConfig) throw new MarkformConfigError(`Unknown provider: "${provider}". Built-in providers: ${Object.keys(PROVIDERS).join(", ")}. To use a custom provider, pass it via the \`providers\` option.`, {
|
|
9808
|
+
option: "model",
|
|
9809
|
+
expectedType: "provider name or custom provider",
|
|
9810
|
+
receivedValue: provider
|
|
9811
|
+
});
|
|
9733
9812
|
const apiKey = process.env[providerConfig.envVar];
|
|
9734
9813
|
if (!apiKey) throw new MarkformConfigError(`Missing API key for "${provider}" provider (model: ${modelIdString}).\nSet the ${providerConfig.envVar} environment variable or add it to your .env file.`, {
|
|
9735
9814
|
option: providerConfig.envVar,
|
|
@@ -10259,11 +10338,13 @@ async function fillForm(options) {
|
|
|
10259
10338
|
}
|
|
10260
10339
|
let model;
|
|
10261
10340
|
let provider;
|
|
10341
|
+
let providerTools;
|
|
10262
10342
|
if (!options._testAgent) try {
|
|
10263
10343
|
if (typeof options.model === "string") {
|
|
10264
|
-
const resolved = await resolveModel(options.model);
|
|
10344
|
+
const resolved = await resolveModel(options.model, options.providers);
|
|
10265
10345
|
model = resolved.model;
|
|
10266
10346
|
provider = resolved.provider;
|
|
10347
|
+
providerTools = resolved.tools;
|
|
10267
10348
|
} else model = options.model;
|
|
10268
10349
|
} catch (error) {
|
|
10269
10350
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -10305,6 +10386,7 @@ async function fillForm(options) {
|
|
|
10305
10386
|
systemPromptAddition: options.systemPromptAddition,
|
|
10306
10387
|
targetRole: targetRoles[0] ?? AGENT_ROLE,
|
|
10307
10388
|
provider,
|
|
10389
|
+
providerTools,
|
|
10308
10390
|
enableWebSearch: options.enableWebSearch,
|
|
10309
10391
|
additionalTools: options.additionalTools,
|
|
10310
10392
|
callbacks: mergedCallbacks,
|
|
@@ -11106,8 +11188,8 @@ function validateResearchForm(form) {
|
|
|
11106
11188
|
//#endregion
|
|
11107
11189
|
//#region src/index.ts
|
|
11108
11190
|
/** Markform version (injected at build time). */
|
|
11109
|
-
const VERSION = "0.1.
|
|
11191
|
+
const VERSION = "0.1.23";
|
|
11110
11192
|
|
|
11111
11193
|
//#endregion
|
|
11112
|
-
export {
|
|
11113
|
-
//# sourceMappingURL=src-
|
|
11194
|
+
export { createMockAgent as A, parseScopeRef as B, getProviderNames as C, FillRecordCollector as D, createLiveAgent as E, findFieldById as F, findAllCheckboxes as G, fieldToJsonSchema as H, getFieldId as I, findAllHeadings as J, injectCheckboxIds as K, isCellRef as L, createHarness as M, coerceInputContext as N, computeExecutionPlan as O, coerceToFieldPatch as P, parseRawTable as Q, isFieldRef as R, getProviderInfo as S, buildMockWireFormat as T, formToJsonSchema as U, serializeScopeRef as V, parseForm as W, parseCellValue as X, findEnclosingHeadings as Y, parseMarkdownTable as Z, fillForm as _, ExecutionMetadataSchema as a, scopeIssuesForItem as b, TimelineEntrySchema as c, ToolCallRecordSchema as d, ToolStatsSchema as f, resolveHarnessConfig as g, formatFillRecordSummary as h, runResearch as i, FormHarness as j, MockAgent as k, TimingBreakdownItemSchema as l, stripUnstableFillRecordFields as m, isResearchForm as n, FillRecordSchema as o, ToolSummarySchema as p, injectHeaderIds as q, validateResearchForm as r, FillRecordStatusSchema as s, VERSION as t, TimingBreakdownSchema as u, ParallelHarness as v, resolveModel as w, BUILT_IN_PROVIDERS as x, createParallelHarness as y, isQualifiedRef as z };
|
|
11195
|
+
//# sourceMappingURL=src-B2uFvGli.mjs.map
|