markform 0.1.21 → 0.1.22

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 (33) hide show
  1. package/README.md +32 -4
  2. package/dist/ai-sdk.d.mts +1 -1
  3. package/dist/ai-sdk.mjs +1 -1
  4. package/dist/{apply-CD-t7ovb.mjs → apply-C7mO7VkZ.mjs} +100 -74
  5. package/dist/apply-C7mO7VkZ.mjs.map +1 -0
  6. package/dist/bin.mjs +1 -1
  7. package/dist/{cli-ChdIy1a7.mjs → cli-C8F9yDsv.mjs} +17 -1213
  8. package/dist/cli-C8F9yDsv.mjs.map +1 -0
  9. package/dist/cli.mjs +1 -1
  10. package/dist/{coreTypes-BQrWf_Wt.d.mts → coreTypes-BlsJkU1w.d.mts} +1 -1
  11. package/dist/fillRecord-DTl5lnK0.d.mts +345 -0
  12. package/dist/fillRecordRenderer-CruJrLkj.mjs +1256 -0
  13. package/dist/fillRecordRenderer-CruJrLkj.mjs.map +1 -0
  14. package/dist/index.d.mts +5 -342
  15. package/dist/index.mjs +3 -3
  16. package/dist/render.d.mts +74 -0
  17. package/dist/render.mjs +4 -0
  18. package/dist/{session-ZgegwtkT.mjs → session-BCcltrLA.mjs} +1 -1
  19. package/dist/{session-ZgegwtkT.mjs.map → session-BCcltrLA.mjs.map} +1 -1
  20. package/dist/{session-BPuQ-ok0.mjs → session-VeSkVrck.mjs} +1 -1
  21. package/dist/{shared-DwdyWmvE.mjs → shared-CsdT2T7k.mjs} +1 -1
  22. package/dist/{shared-DwdyWmvE.mjs.map → shared-CsdT2T7k.mjs.map} +1 -1
  23. package/dist/{shared-BTR35aMz.mjs → shared-fb0nkzQi.mjs} +1 -1
  24. package/dist/{src-DOPe4tmu.mjs → src-CbRnGzMK.mjs} +16 -11
  25. package/dist/{src-DOPe4tmu.mjs.map → src-CbRnGzMK.mjs.map} +1 -1
  26. package/dist/urlFormat-lls7CsEP.mjs +71 -0
  27. package/dist/urlFormat-lls7CsEP.mjs.map +1 -0
  28. package/docs/markform-apis.md +53 -0
  29. package/examples/simple/simple-skipped-filled.report.md +8 -8
  30. package/examples/twitter-thread/twitter-thread.form.md +373 -0
  31. package/package.json +5 -1
  32. package/dist/apply-CD-t7ovb.mjs.map +0 -1
  33. package/dist/cli-ChdIy1a7.mjs.map +0 -1
@@ -0,0 +1,74 @@
1
+
2
+ import { At as ParsedForm } from "./coreTypes-BlsJkU1w.mjs";
3
+ import { r as FillRecord } from "./fillRecord-DTl5lnK0.mjs";
4
+
5
+ //#region src/render/renderUtils.d.ts
6
+ /**
7
+ * Rendering utility functions for HTML generation.
8
+ *
9
+ * Pure utility functions used across all renderers. No CLI or server dependencies.
10
+ */
11
+ /**
12
+ * Escape HTML special characters.
13
+ */
14
+ declare function escapeHtml(str: string): string;
15
+ /**
16
+ * Format milliseconds as human-readable duration.
17
+ */
18
+ declare function formatDuration(ms: number): string;
19
+ /**
20
+ * Format token count with K suffix for large numbers.
21
+ */
22
+ declare function formatTokens(count: number): string;
23
+ //#endregion
24
+ //#region src/render/contentRenderers.d.ts
25
+ /**
26
+ * Render form view content (read-only display of form fields).
27
+ * Used for View tab content.
28
+ */
29
+ declare function renderViewContent(form: ParsedForm): string;
30
+ /**
31
+ * Render source content with Markdown and Jinja syntax highlighting.
32
+ * Used for Source tab content.
33
+ */
34
+ declare function renderSourceContent(content: string): string;
35
+ /**
36
+ * Render markdown content (content only, no page wrapper).
37
+ * Used for tab content.
38
+ */
39
+ declare function renderMarkdownContent(content: string): string;
40
+ /**
41
+ * Render YAML content with syntax highlighting (content only, no page wrapper).
42
+ * Used for tab content.
43
+ */
44
+ declare function renderYamlContent(content: string): string;
45
+ /**
46
+ * Render JSON content with syntax highlighting (content only, no page wrapper).
47
+ * Used for tab content.
48
+ */
49
+ declare function renderJsonContent(content: string): string;
50
+ //#endregion
51
+ //#region src/render/fillRecordRenderer.d.ts
52
+ /**
53
+ * JavaScript for fill record interactive features.
54
+ * Consumers should include this in a <script> tag on their page.
55
+ * Provides: frShowTip(el), frHideTip(), frCopyYaml(btn)
56
+ */
57
+ declare const FILL_RECORD_SCRIPTS = "\n// Copy YAML content handler for Fill Record tab (must be global for dynamically loaded content)\nfunction frCopyYaml(btn) {\n const pre = btn.parentElement.querySelector('pre');\n navigator.clipboard.writeText(pre.textContent).then(() => {\n const orig = btn.textContent;\n btn.textContent = 'Copied!';\n setTimeout(() => btn.textContent = orig, 1500);\n });\n}\n\n// Tooltip handlers for Fill Record visualizations (must be global for dynamically loaded content)\nfunction frShowTip(el) {\n var tip = document.getElementById('fr-tooltip');\n if (tip && el.dataset.tooltip) {\n tip.textContent = el.dataset.tooltip;\n // Position tooltip centered above the element\n var rect = el.getBoundingClientRect();\n tip.style.left = (rect.left + rect.width / 2) + 'px';\n tip.style.top = (rect.top - 8) + 'px';\n tip.style.transform = 'translate(-50%, -100%)';\n tip.classList.add('visible');\n }\n}\nfunction frHideTip() {\n var tip = document.getElementById('fr-tooltip');\n if (tip) tip.classList.remove('visible');\n}\n";
58
+ /**
59
+ * CSS styles for fill record visualization.
60
+ * Uses CSS custom properties for theming (supports dark mode via prefers-color-scheme).
61
+ * Designed to be lightweight, reusable, and embeddable.
62
+ */
63
+ declare const FILL_RECORD_STYLES = "\n<style>\n .fr-dashboard {\n --fr-bg: #ffffff;\n --fr-bg-muted: #f9fafb;\n --fr-bg-subtle: #f3f4f6;\n --fr-border: #e5e7eb;\n --fr-text: #111827;\n --fr-text-muted: #6b7280;\n --fr-primary: #3b82f6;\n --fr-success: #22c55e;\n --fr-warning: #f59e0b;\n --fr-error: #ef4444;\n --fr-info: #6b7280;\n\n /* Typography - consolidated to fewer sizes */\n --fr-font-sm: 13px;\n --fr-font-base: 14px;\n --fr-font-lg: 20px;\n\n font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n padding: 20px;\n max-width: 900px;\n margin: 0 auto;\n color: var(--fr-text);\n line-height: 1.5;\n }\n\n @media (prefers-color-scheme: dark) {\n .fr-dashboard {\n --fr-bg: #1f2937;\n --fr-bg-muted: #374151;\n --fr-bg-subtle: #4b5563;\n --fr-border: #4b5563;\n --fr-text: #f9fafb;\n --fr-text-muted: #9ca3af;\n }\n }\n\n .fr-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 16px;\n padding-bottom: 12px;\n border-bottom: 1px solid var(--fr-border);\n }\n .fr-header__model {\n font-weight: 600;\n font-size: var(--fr-font-base);\n color: var(--fr-text);\n }\n .fr-header__time {\n font-weight: 600;\n font-size: var(--fr-font-base);\n color: var(--fr-text);\n }\n\n .fr-banner {\n border-radius: 8px;\n padding: 12px 16px;\n margin-bottom: 20px;\n font-size: var(--fr-font-base);\n }\n .fr-banner--error {\n background: color-mix(in srgb, var(--fr-error) 10%, var(--fr-bg));\n border: 1px solid var(--fr-error);\n }\n .fr-banner--warning {\n background: color-mix(in srgb, var(--fr-warning) 10%, var(--fr-bg));\n border: 1px solid var(--fr-warning);\n }\n\n .fr-cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n .fr-card {\n padding: 16px;\n background: var(--fr-bg-muted);\n border-radius: 8px;\n text-align: center;\n }\n .fr-card__label {\n font-size: var(--fr-font-sm);\n color: var(--fr-text-muted);\n margin-bottom: 4px;\n }\n .fr-card__value {\n font-size: var(--fr-font-lg);\n font-weight: 600;\n }\n .fr-card__sub {\n font-size: var(--fr-font-sm);\n color: var(--fr-text-muted);\n margin-top: 2px;\n }\n\n .fr-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border-radius: 4px;\n font-weight: 600;\n font-size: var(--fr-font-sm);\n }\n .fr-badge--completed { background: color-mix(in srgb, var(--fr-success) 15%, transparent); color: var(--fr-success); }\n .fr-badge--partial { background: color-mix(in srgb, var(--fr-warning) 15%, transparent); color: var(--fr-warning); }\n .fr-badge--cancelled { background: color-mix(in srgb, var(--fr-info) 15%, transparent); color: var(--fr-info); }\n .fr-badge--failed { background: color-mix(in srgb, var(--fr-error) 15%, transparent); color: var(--fr-error); }\n\n .fr-section {\n margin-bottom: 24px;\n }\n .fr-section__title {\n font-size: var(--fr-font-base);\n font-weight: 500;\n color: var(--fr-text);\n margin-bottom: 8px;\n }\n\n .fr-progress {\n background: var(--fr-border);\n border-radius: 4px;\n height: 20px;\n overflow: hidden;\n }\n .fr-progress__bar {\n background: var(--fr-primary);\n height: 100%;\n transition: width 0.3s ease;\n }\n .fr-progress__text {\n font-size: var(--fr-font-sm);\n color: var(--fr-text-muted);\n margin-top: 4px;\n }\n\n .fr-progress__segments {\n display: flex;\n height: 100%;\n width: 100%;\n }\n .fr-progress-segment {\n height: 100%;\n min-width: 2px;\n border-right: 2px solid var(--fr-bg);\n cursor: pointer;\n }\n .fr-progress-segment:last-child {\n border-right: none;\n }\n .fr-progress-segment--filled {\n background: var(--fr-primary);\n }\n .fr-progress-segment--filled:hover {\n background: color-mix(in srgb, var(--fr-primary) 70%, white);\n }\n .fr-progress-segment--prefilled {\n background: #8b5cf6;\n }\n .fr-progress-segment--prefilled:hover {\n background: color-mix(in srgb, #8b5cf6 70%, white);\n }\n .fr-progress-segment--skipped {\n background: var(--fr-warning);\n }\n .fr-progress-segment--skipped:hover {\n background: color-mix(in srgb, var(--fr-warning) 70%, white);\n }\n .fr-progress-segment--empty {\n background: var(--fr-border);\n }\n\n /* Gantt chart - each call on its own row */\n .fr-gantt {\n margin-bottom: 8px;\n }\n .fr-gantt__row {\n display: flex;\n align-items: center;\n height: 20px;\n margin-bottom: 3px;\n }\n .fr-gantt__label {\n width: 90px;\n flex-shrink: 0;\n font-size: 11px;\n color: var(--fr-text-muted);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n padding-right: 8px;\n text-align: right;\n }\n .fr-gantt__track {\n flex: 1;\n background: var(--fr-bg-subtle);\n border-radius: 3px;\n height: 14px;\n position: relative;\n }\n .fr-gantt__bar {\n position: absolute;\n top: 2px;\n height: calc(100% - 4px);\n min-width: 6px;\n border-radius: 2px;\n cursor: pointer;\n }\n .fr-gantt__bar:hover {\n filter: brightness(1.15);\n }\n .fr-gantt__bar--llm {\n background: var(--fr-primary);\n }\n .fr-gantt__bar--tool {\n background: var(--fr-success);\n }\n .fr-gantt__legend {\n display: flex;\n gap: 16px;\n font-size: var(--fr-font-sm);\n color: var(--fr-text-muted);\n margin-top: 12px;\n padding-top: 8px;\n border-top: 1px solid var(--fr-border);\n }\n .fr-gantt__legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .fr-gantt__legend-dot {\n width: 10px;\n height: 10px;\n border-radius: 2px;\n }\n .fr-gantt__legend-dot--llm { background: var(--fr-primary); }\n .fr-gantt__legend-dot--tool { background: var(--fr-success); }\n\n /* Tooltip container */\n .fr-tooltip {\n position: fixed;\n background: #1f2937;\n color: #f9fafb;\n padding: 8px 12px;\n border-radius: 4px;\n font-size: var(--fr-font-sm);\n white-space: pre-line;\n pointer-events: none;\n z-index: 1000;\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.05s ease-out, visibility 0.05s ease-out;\n }\n .fr-tooltip.visible {\n opacity: 1;\n visibility: visible;\n transition: opacity 0.2s ease-in, visibility 0.2s ease-in;\n }\n\n .fr-table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--fr-font-sm);\n }\n .fr-table th {\n padding: 8px 12px;\n text-align: left;\n font-weight: 600;\n background: var(--fr-bg-subtle);\n }\n .fr-table th:not(:first-child) { text-align: center; }\n .fr-table td {\n padding: 8px 12px;\n border-bottom: 1px solid var(--fr-border);\n }\n .fr-table td:not(:first-child) { text-align: center; }\n\n .fr-details {\n border: none;\n background: none;\n }\n .fr-details > summary {\n cursor: pointer;\n font-size: var(--fr-font-base);\n font-weight: 500;\n color: var(--fr-text);\n padding: 8px 0;\n list-style: none;\n }\n .fr-details > summary::-webkit-details-marker { display: none; }\n .fr-details > summary::before {\n content: '\u25B6';\n display: inline-block;\n margin-right: 8px;\n transition: transform 0.2s;\n font-size: 11px;\n }\n .fr-details[open] > summary::before {\n transform: rotate(90deg);\n }\n .fr-details__content {\n background: var(--fr-bg-muted);\n border-radius: 8px;\n padding: 16px;\n margin-top: 8px;\n }\n\n .fr-turn {\n margin-bottom: 8px;\n background: var(--fr-bg-muted);\n border-radius: 4px;\n }\n .fr-turn summary {\n cursor: pointer;\n padding: 12px;\n font-size: var(--fr-font-sm);\n list-style: none;\n }\n .fr-turn summary::-webkit-details-marker { display: none; }\n .fr-turn summary::before {\n content: '\u25B6';\n display: inline-block;\n margin-right: 8px;\n transition: transform 0.2s;\n font-size: 11px;\n }\n .fr-turn[open] summary::before {\n transform: rotate(90deg);\n }\n .fr-turn__content {\n padding: 0 12px 12px;\n }\n .fr-turn__tools {\n margin: 0;\n padding-left: 20px;\n list-style: none;\n }\n .fr-turn__tool {\n margin: 4px 0;\n font-size: var(--fr-font-sm);\n color: var(--fr-text-muted);\n }\n .fr-turn__tool--error { color: var(--fr-error); }\n\n .fr-turn__query {\n color: var(--fr-primary);\n font-style: italic;\n }\n\n .fr-turn__patches {\n margin: 4px 0 8px 20px;\n padding: 8px 12px;\n background: var(--fr-bg-subtle);\n border-radius: 4px;\n font-size: var(--fr-font-sm);\n }\n .fr-turn__patch {\n margin: 4px 0;\n padding: 4px 0;\n border-bottom: 1px solid var(--fr-border);\n }\n .fr-turn__patch:last-child {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n }\n .fr-turn__patch-field {\n font-weight: 600;\n color: var(--fr-text);\n }\n .fr-turn__patch-op {\n font-size: 11px;\n padding: 1px 4px;\n border-radius: 2px;\n background: var(--fr-bg-muted);\n color: var(--fr-text-muted);\n margin-left: 6px;\n }\n .fr-turn__patch-value {\n display: block;\n margin-top: 2px;\n color: var(--fr-text-muted);\n font-family: ui-monospace, 'SF Mono', Menlo, monospace;\n word-break: break-word;\n white-space: pre-wrap;\n max-height: 200px;\n overflow: auto;\n }\n .fr-turn__patch-value--skip {\n color: var(--fr-warning);\n font-style: italic;\n }\n .fr-turn__patch-value--clear {\n color: var(--fr-info);\n font-style: italic;\n }\n\n .fr-raw {\n position: relative;\n }\n .fr-copy-btn {\n position: absolute;\n top: 8px;\n right: 8px;\n padding: 4px 8px;\n font-size: var(--fr-font-sm);\n background: var(--fr-bg-subtle);\n border: 1px solid var(--fr-border);\n border-radius: 4px;\n cursor: pointer;\n color: var(--fr-text-muted);\n transition: all 0.15s;\n }\n .fr-copy-btn:hover {\n background: var(--fr-border);\n color: var(--fr-text);\n }\n .fr-copy-btn:active {\n transform: scale(0.95);\n }\n\n /* Scoped pre styles to override parent .tab-content pre */\n .fr-dashboard pre {\n background: var(--fr-bg-muted);\n color: var(--fr-text);\n padding: 1rem;\n border-radius: 6px;\n border: 1px solid var(--fr-border);\n overflow-x: auto;\n font-family: ui-monospace, 'SF Mono', Menlo, monospace;\n font-size: 0.85rem;\n line-height: 1.5;\n margin: 0;\n }\n\n /* Override syntax highlighting colors for dark mode compatibility */\n .fr-dashboard .syn-key { color: var(--fr-primary); }\n .fr-dashboard .syn-string { color: var(--fr-success); }\n .fr-dashboard .syn-number { color: var(--fr-primary); }\n .fr-dashboard .syn-bool { color: var(--fr-warning); }\n .fr-dashboard .syn-null { color: var(--fr-error); }\n\n @media (max-width: 600px) {\n .fr-dashboard { padding: 12px; }\n .fr-cards { grid-template-columns: repeat(2, 1fr); gap: 12px; }\n .fr-card { padding: 12px; }\n .fr-card__value { font-size: 18px; }\n .fr-table { font-size: var(--fr-font-sm); }\n .fr-table th, .fr-table td { padding: 6px 8px; }\n }\n</style>\n";
64
+ /**
65
+ * Render fill record content (dashboard-style visualization).
66
+ * Uses CSS custom properties for theming with automatic dark mode support.
67
+ * Mobile responsive with grid-based layout.
68
+ *
69
+ * @public Exported for testing and reuse.
70
+ */
71
+ declare function renderFillRecordContent(record: FillRecord): string;
72
+ //#endregion
73
+ export { FILL_RECORD_SCRIPTS, FILL_RECORD_STYLES, escapeHtml, formatDuration, formatTokens, renderFillRecordContent, renderJsonContent, renderMarkdownContent, renderSourceContent, renderViewContent, renderYamlContent };
74
+ //# sourceMappingURL=render.d.mts.map
@@ -0,0 +1,4 @@
1
+
2
+ import { a as renderJsonContent, c as renderViewContent, d as formatDuration, f as formatTokens, l as renderYamlContent, n as FILL_RECORD_STYLES, o as renderMarkdownContent, r as renderFillRecordContent, s as renderSourceContent, t as FILL_RECORD_SCRIPTS, u as escapeHtml } from "./fillRecordRenderer-CruJrLkj.mjs";
3
+
4
+ export { FILL_RECORD_SCRIPTS, FILL_RECORD_STYLES, escapeHtml, formatDuration, formatTokens, renderFillRecordContent, renderJsonContent, renderMarkdownContent, renderSourceContent, renderViewContent, renderYamlContent };
@@ -113,4 +113,4 @@ function toSnakeCaseDeep(obj, preserveKeys = false) {
113
113
 
114
114
  //#endregion
115
115
  export { serializeSession as n, parseSession as t };
116
- //# sourceMappingURL=session-ZgegwtkT.mjs.map
116
+ //# sourceMappingURL=session-BCcltrLA.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-ZgegwtkT.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"}
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"}
@@ -1,4 +1,4 @@
1
1
 
2
- import { n as serializeSession, t as parseSession } from "./session-ZgegwtkT.mjs";
2
+ import { n as serializeSession, t as parseSession } from "./session-BCcltrLA.mjs";
3
3
 
4
4
  export { serializeSession };
@@ -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-DwdyWmvE.mjs.map
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-DwdyWmvE.mjs";
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 { $ as transformHarnessConfigToTs, A as parseOptionText, B as DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN, C as extractTableContent, D as getStringAttr, E as getStringArrayAttr, F as DEFAULT_MAX_PATCHES_PER_TURN, H as DEFAULT_ROLES, I as DEFAULT_MAX_STEPS_PER_TURN, L as DEFAULT_MAX_TURNS, N as DEFAULT_MAX_ISSUES_PER_TURN, O as getValidateAttr, P as DEFAULT_MAX_PARALLEL_AGENTS, S as extractOptionItems, T as getNumberAttr, U as DEFAULT_ROLE_INSTRUCTIONS, V as DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN, _ as preprocessCommentSyntax, b as CHECKBOX_MARKERS, c as computeProgressSummary, d as serializeForm, g as detectSyntaxStyle, i as inspect, j as AGENT_ROLE, k as isTagNode, l as computeStructureSummary, r as getFieldsForRoles, rt as getWebSearchConfig, st as MarkformConfigError, t as applyPatches, ut as MarkformParseError, w as getBooleanAttr, x as extractFenceValue, y as tryParseSentinelResponse, z as DEFAULT_PRIORITY } from "./apply-CD-t7ovb.mjs";
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-C7mO7VkZ.mjs";
4
4
  import { z } from "zod";
5
5
  import Markdoc from "@markdoc/markdoc";
6
6
  import YAML from "yaml";
@@ -9193,14 +9193,19 @@ var LiveAgent = class {
9193
9193
  executionId: this.executionId
9194
9194
  });
9195
9195
  } catch {}
9196
- const result = await generateText({
9197
- model: this.model,
9198
- system: systemPrompt,
9199
- prompt: contextPrompt,
9200
- tools,
9201
- toolChoice: this.toolChoice,
9202
- stopWhen: stepCountIs(this.maxStepsPerTurn)
9203
- });
9196
+ let result;
9197
+ try {
9198
+ result = await generateText({
9199
+ model: this.model,
9200
+ system: systemPrompt,
9201
+ prompt: contextPrompt,
9202
+ tools,
9203
+ toolChoice: this.toolChoice,
9204
+ stopWhen: stepCountIs(this.maxStepsPerTurn)
9205
+ });
9206
+ } catch (error) {
9207
+ throw wrapApiError(error, this.provider ?? "unknown", modelId);
9208
+ }
9204
9209
  if (this.callbacks?.onLlmCallEnd) try {
9205
9210
  this.callbacks.onLlmCallEnd({
9206
9211
  model: modelId,
@@ -11106,8 +11111,8 @@ function validateResearchForm(form) {
11106
11111
  //#endregion
11107
11112
  //#region src/index.ts
11108
11113
  /** Markform version (injected at build time). */
11109
- const VERSION = "0.1.21";
11114
+ const VERSION = "0.1.22";
11110
11115
 
11111
11116
  //#endregion
11112
11117
  export { FormHarness as A, serializeScopeRef as B, resolveModel as C, computeExecutionPlan as D, FillRecordCollector as E, getFieldId as F, injectCheckboxIds as G, formToJsonSchema as H, isCellRef as I, findEnclosingHeadings as J, injectHeaderIds as K, isFieldRef as L, coerceInputContext as M, coerceToFieldPatch as N, MockAgent as O, findFieldById as P, isQualifiedRef as R, getProviderNames as S, createLiveAgent as T, parseForm as U, fieldToJsonSchema as V, findAllCheckboxes as W, parseMarkdownTable as X, parseCellValue as Y, parseRawTable 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, createHarness as j, createMockAgent as k, TimingBreakdownItemSchema as l, stripUnstableFillRecordFields as m, isResearchForm as n, FillRecordSchema as o, ToolSummarySchema as p, findAllHeadings as q, validateResearchForm as r, FillRecordStatusSchema as s, VERSION as t, TimingBreakdownSchema as u, ParallelHarness as v, buildMockWireFormat as w, getProviderInfo as x, createParallelHarness as y, parseScopeRef as z };
11113
- //# sourceMappingURL=src-DOPe4tmu.mjs.map
11118
+ //# sourceMappingURL=src-CbRnGzMK.mjs.map