markform 0.1.11 → 0.1.12

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/dist/ai-sdk.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  import { L as PatchSchema } from "./coreTypes-Z8SvQyoL.mjs";
3
- import { d as serializeForm, i as inspect, t as applyPatches } from "./apply-yfRtNIHc.mjs";
3
+ import { d as serializeForm, i as inspect, t as applyPatches } from "./apply-WeeBXwXg.mjs";
4
4
  import { z } from "zod";
5
5
 
6
6
  //#region src/integrations/vercelAiSdkTools.ts
@@ -2,7 +2,7 @@
2
2
  import YAML from "yaml";
3
3
 
4
4
  //#region src/errors.ts
5
- const VERSION = "0.1.11";
5
+ const VERSION = "0.1.12";
6
6
  /**
7
7
  * Base error class for all markform errors.
8
8
  * Consumers can catch this to handle any markform error.
package/dist/bin.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
 
4
- import { t as runCli } from "./cli-Dtjcg98Y.mjs";
4
+ import { t as runCli } from "./cli-R_mVVf4x.mjs";
5
5
  import { resolve } from "node:path";
6
6
  import { existsSync } from "node:fs";
7
7
  import { config } from "dotenv";
@@ -1,7 +1,7 @@
1
1
 
2
2
  import { L as PatchSchema } from "./coreTypes-Z8SvQyoL.mjs";
3
- import { A as deriveReportPath, C as DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN, D as REPORT_EXTENSION, E as MAX_FORMS_IN_MENU, F as WEB_SEARCH_CONFIG, I as formatSuggestedLlms, M as detectFileType, N as parseRolesFlag, O as USER_ROLE, P as SUGGESTED_LLMS, R as hasWebSearchSupport, S as DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN, _ as DEFAULT_MAX_ISSUES_PER_TURN, b as DEFAULT_PORT, d as serializeForm, f as serializeRawMarkdown, g as DEFAULT_FORMS_DIR, h as ALL_EXTENSIONS, i as inspect, j as deriveSchemaPath, k as deriveExportPath, m as AGENT_ROLE, n as getAllFields, p as serializeReport, t as applyPatches, v as DEFAULT_MAX_PATCHES_PER_TURN, y as DEFAULT_MAX_TURNS, z as parseModelIdForDisplay } from "./apply-yfRtNIHc.mjs";
4
- import { D as parseForm, E as formToJsonSchema, a as resolveHarnessConfig, c as getProviderNames, d as createLiveAgent, h as createHarness, i as runResearch, l as resolveModel, n as isResearchForm, o as fillForm, p as createMockAgent, s as getProviderInfo, t as VERSION, u as buildMockWireFormat } from "./src-ozWOSQTW.mjs";
3
+ import { A as deriveReportPath, C as DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN, D as REPORT_EXTENSION, E as MAX_FORMS_IN_MENU, F as WEB_SEARCH_CONFIG, I as formatSuggestedLlms, M as detectFileType, N as parseRolesFlag, O as USER_ROLE, P as SUGGESTED_LLMS, R as hasWebSearchSupport, S as DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN, _ as DEFAULT_MAX_ISSUES_PER_TURN, b as DEFAULT_PORT, d as serializeForm, f as serializeRawMarkdown, g as DEFAULT_FORMS_DIR, h as ALL_EXTENSIONS, i as inspect, j as deriveSchemaPath, k as deriveExportPath, m as AGENT_ROLE, n as getAllFields, p as serializeReport, t as applyPatches, v as DEFAULT_MAX_PATCHES_PER_TURN, y as DEFAULT_MAX_TURNS, z as parseModelIdForDisplay } from "./apply-WeeBXwXg.mjs";
4
+ import { D as parseForm, E as formToJsonSchema, a as resolveHarnessConfig, c as getProviderNames, d as createLiveAgent, h as createHarness, i as runResearch, l as resolveModel, n as isResearchForm, o as fillForm, p as createMockAgent, s as getProviderInfo, t as VERSION, u as buildMockWireFormat } from "./src-DMQCFp2l.mjs";
5
5
  import { n as serializeSession } from "./session-DX-DvjRP.mjs";
6
6
  import { a as formatPath, c as logError, d as logTiming, f as logVerbose, g as writeFile, i as formatOutput, l as logInfo, m as readFile$1, n as createSpinner, o as getCommandContext, p as logWarn, r as ensureFormsDir, s as logDryRun, t as OUTPUT_FORMATS, u as logSuccess } from "./shared-D3dNi-Gn.mjs";
7
7
  import Markdoc from "@markdoc/markdoc";
@@ -2253,7 +2253,7 @@ async function runAgentFillWorkflow(form, modelId, formsDir, filePath, isResearc
2253
2253
  const result = await fillForm({
2254
2254
  form,
2255
2255
  model: modelId,
2256
- maxTurns,
2256
+ maxTurnsTotal: maxTurns,
2257
2257
  maxPatchesPerTurn,
2258
2258
  maxIssuesPerTurn,
2259
2259
  targetRoles: [AGENT_ROLE],
@@ -5089,7 +5089,7 @@ function registerResearchCommand(program) {
5089
5089
  model: modelId,
5090
5090
  enableWebSearch: true,
5091
5091
  captureWireFormat: false,
5092
- maxTurns,
5092
+ maxTurnsTotal: maxTurns,
5093
5093
  maxPatchesPerTurn,
5094
5094
  maxIssuesPerTurn,
5095
5095
  targetRoles: [AGENT_ROLE],
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
 
2
- import { t as runCli } from "./cli-Dtjcg98Y.mjs";
2
+ import { t as runCli } from "./cli-R_mVVf4x.mjs";
3
3
 
4
4
  export { runCli };
package/dist/index.d.mts CHANGED
@@ -850,8 +850,32 @@ interface FillOptions {
850
850
  inputContext?: InputContext;
851
851
  /** Additional context to append to the composed system prompt (never overrides) */
852
852
  systemPromptAddition?: string;
853
- /** Maximum harness turns (default: 100) */
854
- maxTurns?: number;
853
+ /**
854
+ * Maximum TOTAL turns across all calls combined.
855
+ * This is a safety limit to prevent runaway sessions.
856
+ * When resuming, pass the same value—the limit is enforced by comparing
857
+ * against `startingTurnNumber + turnsExecutedThisCall`.
858
+ *
859
+ * @default 100
860
+ */
861
+ maxTurnsTotal?: number;
862
+ /**
863
+ * Maximum turns to execute in THIS call only.
864
+ * When reached, returns with status `{ ok: false, reason: 'batch_limit' }`.
865
+ * Caller can resume by passing the returned form markdown back.
866
+ *
867
+ * Use for orchestrated environments with timeout constraints (e.g., Convex, Step Functions).
868
+ *
869
+ * @default undefined (no per-call limit - runs until complete or maxTurnsTotal)
870
+ */
871
+ maxTurnsThisCall?: number;
872
+ /**
873
+ * Starting turn number for progress tracking when resuming.
874
+ * Affects callback turn numbers and FillResult.turns calculation.
875
+ *
876
+ * @default 0
877
+ */
878
+ startingTurnNumber?: number;
855
879
  /** Maximum patches per turn (default: 20) */
856
880
  maxPatchesPerTurn?: number;
857
881
  /** Maximum issues to show per turn (default: 10) */
@@ -917,12 +941,18 @@ interface TurnProgress {
917
941
  }
918
942
  /**
919
943
  * Fill status indicating success or failure reason.
944
+ *
945
+ * - `ok: true` - Form completed successfully
946
+ * - `max_turns` - Hit overall maxTurnsTotal safety limit
947
+ * - `batch_limit` - Hit maxTurnsThisCall per-call limit (resume by calling again)
948
+ * - `cancelled` - Aborted via signal
949
+ * - `error` - Unexpected error
920
950
  */
921
951
  type FillStatus = {
922
952
  ok: true;
923
953
  } | {
924
954
  ok: false;
925
- reason: 'max_turns' | 'cancelled' | 'error';
955
+ reason: 'max_turns' | 'batch_limit' | 'cancelled' | 'error';
926
956
  message?: string;
927
957
  };
928
958
  /**
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  import { $ as SetUrlListPatchSchema, A as MultiCheckboxStateSchema, At as WireToolResultSchema, B as ProgressSummarySchema, C as HarnessConfigSchema, Ct as ValidationIssueSchema, D as IssueReasonSchema, Dt as WireResponseFormatSchema, E as InspectResultSchema, Et as WireRequestFormatSchema, F as OptionIdSchema, G as SetCheckboxesPatchSchema, H as SessionFinalSchema, I as OptionSchema, J as SetNumberPatchSchema, K as SetDatePatchSchema, L as PatchSchema, M as MultiSelectValueSchema, Mt as YearValueSchema, N as NumberFieldSchema, O as IssueScopeSchema, Ot as WireResponseStepSchema, P as NumberValueSchema, Q as SetTablePatchSchema, R as ProgressCountsSchema, S as FormSchemaSchema, St as UrlValueSchema, T as InspectIssueSchema, Tt as WireFormatSchema, U as SessionTranscriptSchema, V as RunModeSchema, W as SessionTurnSchema, X as SetStringListPatchSchema, Y as SetSingleSelectPatchSchema, Z as SetStringPatchSchema, _ as FieldKindSchema, _t as TableRowResponseSchema, a as CheckboxProgressCountsSchema, at as SingleSelectValueSchema, b as FieldSchema, bt as UrlListFieldSchema, c as CheckboxesValueSchema, ct as StepResultSchema, d as DateFieldSchema, dt as StringListValueSchema, et as SetUrlPatchSchema, f as DateValueSchema, ft as StringValueSchema, g as FieldGroupSchema, gt as TableRowPatchSchema, h as ExplicitCheckboxValueSchema, ht as TableFieldSchema, i as CheckboxModeSchema, it as SingleSelectFieldSchema, j as MultiSelectFieldSchema, jt as YearFieldSchema, k as MarkformFrontmatterSchema, kt as WireToolCallSchema, l as ClearFieldPatchSchema, lt as StringFieldSchema, m as DocumentationTagSchema, mt as TableColumnSchema, n as ApplyResultSchema, nt as SeveritySchema, o as CheckboxValueSchema, ot as SourcePositionSchema, p as DocumentationBlockSchema, pt as StructureSummarySchema, q as SetMultiSelectPatchSchema, r as CellResponseSchema, rt as SimpleCheckboxStateSchema, s as CheckboxesFieldSchema, st as SourceRangeSchema, t as AnswerStateSchema, tt as SetYearPatchSchema, u as ColumnTypeNameSchema, ut as StringListFieldSchema, v as FieldProgressSchema, vt as TableValueSchema, w as IdSchema, wt as ValidatorRefSchema, x as FieldValueSchema, xt as UrlListValueSchema, y as FieldResponseSchema, yt as UrlFieldSchema, z as ProgressStateSchema } from "./coreTypes-Z8SvQyoL.mjs";
3
- import { $ as isPatchError, B as MarkformAbortError, G as MarkformPatchError, H as MarkformError, J as isAbortError, K as MarkformValidationError, Q as isParseError, U as MarkformLlmError, V as MarkformConfigError, W as MarkformParseError, X as isLlmError, Y as isConfigError, Z as isMarkformError, a as validate, c as computeProgressSummary, d as serializeForm, et as isRetryableError, i as inspect, l as computeStructureSummary, o as computeAllSummaries, p as serializeReport, q as ParseError, s as computeFormState, t as applyPatches, tt as isValidationError, u as isFormComplete } from "./apply-yfRtNIHc.mjs";
4
- import { A as parseRawTable, C as parseScopeRef, D as parseForm, E as formToJsonSchema, O as parseCellValue, S as isQualifiedRef, T as fieldToJsonSchema, _ as coerceToFieldPatch, a as resolveHarnessConfig, b as isCellRef, f as MockAgent, g as coerceInputContext, h as createHarness, i as runResearch, k as parseMarkdownTable, m as FormHarness, n as isResearchForm, o as fillForm, p as createMockAgent, r as validateResearchForm, t as VERSION, v as findFieldById, w as serializeScopeRef, x as isFieldRef, y as getFieldId } from "./src-ozWOSQTW.mjs";
3
+ import { $ as isPatchError, B as MarkformAbortError, G as MarkformPatchError, H as MarkformError, J as isAbortError, K as MarkformValidationError, Q as isParseError, U as MarkformLlmError, V as MarkformConfigError, W as MarkformParseError, X as isLlmError, Y as isConfigError, Z as isMarkformError, a as validate, c as computeProgressSummary, d as serializeForm, et as isRetryableError, i as inspect, l as computeStructureSummary, o as computeAllSummaries, p as serializeReport, q as ParseError, s as computeFormState, t as applyPatches, tt as isValidationError, u as isFormComplete } from "./apply-WeeBXwXg.mjs";
4
+ import { A as parseRawTable, C as parseScopeRef, D as parseForm, E as formToJsonSchema, O as parseCellValue, S as isQualifiedRef, T as fieldToJsonSchema, _ as coerceToFieldPatch, a as resolveHarnessConfig, b as isCellRef, f as MockAgent, g as coerceInputContext, h as createHarness, i as runResearch, k as parseMarkdownTable, m as FormHarness, n as isResearchForm, o as fillForm, p as createMockAgent, r as validateResearchForm, t as VERSION, v as findFieldById, w as serializeScopeRef, x as isFieldRef, y as getFieldId } from "./src-DMQCFp2l.mjs";
5
5
  import { n as serializeSession, t as parseSession } from "./session-DX-DvjRP.mjs";
6
6
 
7
7
  export { AnswerStateSchema, ApplyResultSchema, CellResponseSchema, CheckboxModeSchema, CheckboxProgressCountsSchema, CheckboxValueSchema, CheckboxesFieldSchema, CheckboxesValueSchema, ClearFieldPatchSchema, ColumnTypeNameSchema, DateFieldSchema, DateValueSchema, DocumentationBlockSchema, DocumentationTagSchema, ExplicitCheckboxValueSchema, FieldGroupSchema, FieldKindSchema, FieldProgressSchema, FieldResponseSchema, FieldSchema, FieldValueSchema, FormHarness, FormSchemaSchema, HarnessConfigSchema, IdSchema, InspectIssueSchema, InspectResultSchema, IssueReasonSchema, IssueScopeSchema, MarkformAbortError, MarkformConfigError, MarkformError, MarkformFrontmatterSchema, MarkformLlmError, MarkformParseError, MarkformPatchError, MarkformValidationError, MockAgent, MultiCheckboxStateSchema, MultiSelectFieldSchema, MultiSelectValueSchema, NumberFieldSchema, NumberValueSchema, OptionIdSchema, OptionSchema, ParseError, PatchSchema, ProgressCountsSchema, ProgressStateSchema, ProgressSummarySchema, RunModeSchema, SessionFinalSchema, SessionTranscriptSchema, SessionTurnSchema, SetCheckboxesPatchSchema, SetDatePatchSchema, SetMultiSelectPatchSchema, SetNumberPatchSchema, SetSingleSelectPatchSchema, SetStringListPatchSchema, SetStringPatchSchema, SetTablePatchSchema, SetUrlListPatchSchema, SetUrlPatchSchema, SetYearPatchSchema, SeveritySchema, SimpleCheckboxStateSchema, SingleSelectFieldSchema, SingleSelectValueSchema, SourcePositionSchema, SourceRangeSchema, StepResultSchema, StringFieldSchema, StringListFieldSchema, StringListValueSchema, StringValueSchema, StructureSummarySchema, TableColumnSchema, TableFieldSchema, TableRowPatchSchema, TableRowResponseSchema, TableValueSchema, UrlFieldSchema, UrlListFieldSchema, UrlListValueSchema, UrlValueSchema, VERSION, ValidationIssueSchema, ValidatorRefSchema, WireFormatSchema, WireRequestFormatSchema, WireResponseFormatSchema, WireResponseStepSchema, WireToolCallSchema, WireToolResultSchema, YearFieldSchema, YearValueSchema, applyPatches, coerceInputContext, coerceToFieldPatch, computeAllSummaries, computeFormState, computeProgressSummary, computeStructureSummary, createHarness, createMockAgent, fieldToJsonSchema, fillForm, findFieldById, formToJsonSchema, getFieldId, inspect, isAbortError, isCellRef, isConfigError, isFieldRef, isFormComplete, isLlmError, isMarkformError, isParseError, isPatchError, isQualifiedRef, isResearchForm, isRetryableError, isValidationError, parseCellValue, parseForm, parseMarkdownTable, parseRawTable, parseScopeRef, parseSession, resolveHarnessConfig, runResearch, serializeForm, serializeReport, serializeScopeRef, serializeSession, validate, validateResearchForm };
@@ -1,6 +1,6 @@
1
1
 
2
2
  import { L as PatchSchema, V as RunModeSchema } from "./coreTypes-Z8SvQyoL.mjs";
3
- import { C as DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN, L as getWebSearchConfig, S as DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN, T as DEFAULT_ROLE_INSTRUCTIONS, V as MarkformConfigError, W as MarkformParseError, _ as DEFAULT_MAX_ISSUES_PER_TURN, d as serializeForm, i as inspect, m as AGENT_ROLE, r as getFieldsForRoles, t as applyPatches, v as DEFAULT_MAX_PATCHES_PER_TURN, w as DEFAULT_ROLES, x as DEFAULT_PRIORITY, y as DEFAULT_MAX_TURNS } from "./apply-yfRtNIHc.mjs";
3
+ import { C as DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN, L as getWebSearchConfig, S as DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN, T as DEFAULT_ROLE_INSTRUCTIONS, V as MarkformConfigError, W as MarkformParseError, _ as DEFAULT_MAX_ISSUES_PER_TURN, d as serializeForm, i as inspect, m as AGENT_ROLE, r as getFieldsForRoles, t as applyPatches, v as DEFAULT_MAX_PATCHES_PER_TURN, w as DEFAULT_ROLES, x as DEFAULT_PRIORITY, y as DEFAULT_MAX_TURNS } from "./apply-WeeBXwXg.mjs";
4
4
  import { z } from "zod";
5
5
  import Markdoc from "@markdoc/markdoc";
6
6
  import YAML from "yaml";
@@ -7916,15 +7916,15 @@ function getIssuesIntro(issueCount) {
7916
7916
  const PATCH_FORMATS = {
7917
7917
  string: "{ op: \"set_string\", fieldId: \"...\", value: \"...\" }",
7918
7918
  number: "{ op: \"set_number\", fieldId: \"...\", value: 123 }",
7919
- string_list: "{ op: \"set_string_list\", fieldId: \"...\", items: [\"...\", \"...\"] }",
7920
- single_select: "{ op: \"set_single_select\", fieldId: \"...\", selected: \"option_id\" }",
7921
- multi_select: "{ op: \"set_multi_select\", fieldId: \"...\", selected: [\"opt1\", \"opt2\"] }",
7922
- checkboxes: "{ op: \"set_checkboxes\", fieldId: \"...\", values: { \"opt1\": \"done\", \"opt2\": \"todo\" } }",
7919
+ string_list: "{ op: \"set_string_list\", fieldId: \"...\", value: [\"...\", \"...\"] }",
7920
+ single_select: "{ op: \"set_single_select\", fieldId: \"...\", value: \"option_id\" }",
7921
+ multi_select: "{ op: \"set_multi_select\", fieldId: \"...\", value: [\"opt1\", \"opt2\"] }",
7922
+ checkboxes: "{ op: \"set_checkboxes\", fieldId: \"...\", value: { \"opt1\": \"done\", \"opt2\": \"todo\" } }",
7923
7923
  url: "{ op: \"set_url\", fieldId: \"...\", value: \"https://...\" }",
7924
- url_list: "{ op: \"set_url_list\", fieldId: \"...\", items: [\"https://...\", \"https://...\"] }",
7924
+ url_list: "{ op: \"set_url_list\", fieldId: \"...\", value: [\"https://...\", \"https://...\"] }",
7925
7925
  date: "{ op: \"set_date\", fieldId: \"...\", value: \"2024-01-15\" }",
7926
7926
  year: "{ op: \"set_year\", fieldId: \"...\", value: 2024 }",
7927
- table: "{ op: \"set_table\", fieldId: \"...\", rows: [{ col1: \"value1\", col2: \"value2\" }, ...] }"
7927
+ table: "{ op: \"set_table\", fieldId: \"...\", value: [{ col1: \"value1\", col2: \"value2\" }, ...] }"
7928
7928
  };
7929
7929
  /**
7930
7930
  * Get the correct patch format for a field kind.
@@ -8708,12 +8708,14 @@ async function fillForm(options) {
8708
8708
  }
8709
8709
  inputContextWarnings = coercionResult.warnings;
8710
8710
  }
8711
- const maxTurns = options.maxTurns ?? DEFAULT_MAX_TURNS;
8711
+ const maxTurnsTotal = options.maxTurnsTotal ?? DEFAULT_MAX_TURNS;
8712
+ const startingTurnNumber = options.startingTurnNumber ?? 0;
8712
8713
  const maxPatchesPerTurn = options.maxPatchesPerTurn ?? DEFAULT_MAX_PATCHES_PER_TURN;
8713
8714
  const maxIssuesPerTurn = options.maxIssuesPerTurn ?? DEFAULT_MAX_ISSUES_PER_TURN;
8714
8715
  const targetRoles = options.targetRoles ?? [AGENT_ROLE];
8716
+ const remainingTurns = Math.max(0, maxTurnsTotal - startingTurnNumber);
8715
8717
  const harness = createHarness(form, {
8716
- maxTurns,
8718
+ maxTurns: remainingTurns,
8717
8719
  maxPatchesPerTurn,
8718
8720
  maxIssuesPerTurn,
8719
8721
  targetRoles,
@@ -8728,10 +8730,16 @@ async function fillForm(options) {
8728
8730
  additionalTools: options.additionalTools,
8729
8731
  callbacks: options.callbacks
8730
8732
  });
8731
- let turnCount = 0;
8733
+ let turnCount = startingTurnNumber;
8734
+ let turnsThisCall = 0;
8732
8735
  let stepResult = harness.step();
8733
8736
  let previousRejections;
8734
8737
  while (!stepResult.isComplete && !harness.hasReachedMaxTurns()) {
8738
+ if (options.maxTurnsThisCall !== void 0 && turnsThisCall >= options.maxTurnsThisCall) return buildResult(form, turnCount, totalPatches, {
8739
+ ok: false,
8740
+ reason: "batch_limit",
8741
+ message: `Reached per-call limit (${options.maxTurnsThisCall} turns)`
8742
+ }, inputContextWarnings, stepResult.issues);
8735
8743
  if (options.signal?.aborted) return buildResult(form, turnCount, totalPatches, {
8736
8744
  ok: false,
8737
8745
  reason: "cancelled"
@@ -8779,6 +8787,7 @@ async function fillForm(options) {
8779
8787
  const actualPatchesApplied = stepResult.patchesApplied ?? patches.length;
8780
8788
  totalPatches += actualPatchesApplied;
8781
8789
  turnCount++;
8790
+ turnsThisCall++;
8782
8791
  previousRejections = stepResult.rejectedPatches;
8783
8792
  if (options.callbacks?.onTurnComplete) try {
8784
8793
  const requiredIssues = stepResult.issues.filter((i) => i.severity === "required");
@@ -8800,7 +8809,7 @@ async function fillForm(options) {
8800
8809
  return buildResult(form, turnCount, totalPatches, {
8801
8810
  ok: false,
8802
8811
  reason: "max_turns",
8803
- message: `Reached maximum turns (${maxTurns})`
8812
+ message: `Reached maximum total turns (${maxTurnsTotal})`
8804
8813
  }, inputContextWarnings, stepResult.issues);
8805
8814
  }
8806
8815
 
@@ -8821,7 +8830,7 @@ async function fillForm(options) {
8821
8830
  function resolveHarnessConfig(form, options) {
8822
8831
  const frontmatterConfig = form.metadata?.harnessConfig;
8823
8832
  return {
8824
- maxTurns: options?.maxTurns ?? frontmatterConfig?.maxTurns ?? DEFAULT_MAX_TURNS,
8833
+ maxTurns: options?.maxTurnsTotal ?? frontmatterConfig?.maxTurns ?? DEFAULT_MAX_TURNS,
8825
8834
  maxPatchesPerTurn: options?.maxPatchesPerTurn ?? frontmatterConfig?.maxPatchesPerTurn ?? DEFAULT_MAX_PATCHES_PER_TURN,
8826
8835
  maxIssuesPerTurn: options?.maxIssuesPerTurn ?? frontmatterConfig?.maxIssuesPerTurn ?? DEFAULT_MAX_ISSUES_PER_TURN,
8827
8836
  targetRoles: options?.targetRoles,
@@ -8849,7 +8858,7 @@ async function runResearch(form, options) {
8849
8858
  const { model, provider } = await resolveModel(modelSpec);
8850
8859
  const config = {
8851
8860
  ...resolveHarnessConfig(form, options),
8852
- maxTurns: options.maxTurns ?? form.metadata?.harnessConfig?.maxTurns ?? DEFAULT_MAX_TURNS,
8861
+ maxTurns: options.maxTurnsTotal ?? form.metadata?.harnessConfig?.maxTurns ?? DEFAULT_MAX_TURNS,
8853
8862
  maxIssuesPerTurn: options.maxIssuesPerTurn ?? form.metadata?.harnessConfig?.maxIssuesPerTurn ?? DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN,
8854
8863
  maxPatchesPerTurn: options.maxPatchesPerTurn ?? form.metadata?.harnessConfig?.maxPatchesPerTurn ?? DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN,
8855
8864
  targetRoles: options.targetRoles ?? [AGENT_ROLE],
@@ -8948,7 +8957,7 @@ function validateResearchForm(form) {
8948
8957
  //#endregion
8949
8958
  //#region src/index.ts
8950
8959
  /** Markform version (injected at build time). */
8951
- const VERSION = "0.1.11";
8960
+ const VERSION = "0.1.12";
8952
8961
 
8953
8962
  //#endregion
8954
8963
  export { parseRawTable as A, parseScopeRef as C, parseForm as D, formToJsonSchema as E, parseCellValue as O, isQualifiedRef as S, fieldToJsonSchema as T, coerceToFieldPatch as _, resolveHarnessConfig as a, isCellRef as b, getProviderNames as c, createLiveAgent as d, MockAgent as f, coerceInputContext as g, createHarness as h, runResearch as i, parseMarkdownTable as k, resolveModel as l, FormHarness as m, isResearchForm as n, fillForm as o, createMockAgent as p, validateResearchForm as r, getProviderInfo as s, VERSION as t, buildMockWireFormat as u, findFieldById as v, serializeScopeRef as w, isFieldRef as x, getFieldId as y };
@@ -53,11 +53,13 @@ Generate a filtered markdown report suitable for sharing.
53
53
  Produces clean, readable markdown with:
54
54
 
55
55
  - Fields and groups with `report=false` excluded
56
+
56
57
  - Documentation blocks with `report=false` excluded
58
+
57
59
  - Instructions blocks excluded by default (unless `report=true`)
58
60
 
59
- This is useful for generating shareable reports from completed forms without
60
- internal instructions or agent-only content.
61
+ This is useful for generating shareable reports from completed forms without internal
62
+ instructions or agent-only content.
61
63
 
62
64
  ### validate(form: ParsedForm, options?: ValidateOptions): ValidateResult
63
65
 
@@ -127,14 +129,15 @@ Each patch has an `op` and `fieldId`.
127
129
  | --- | --- | --- |
128
130
  | `set_string` | string | `{ "op": "set_string", "fieldId": "name", "value": "Alice" }` |
129
131
  | `set_number` | number | `{ "op": "set_number", "fieldId": "age", "value": 25 }` |
130
- | `set_string_list` | string_list | `{ "op": "set_string_list", "fieldId": "tags", "items": ["a", "b"] }` |
131
- | `set_single_select` | single_select | `{ "op": "set_single_select", "fieldId": "rating", "selected": "high" }` |
132
- | `set_multi_select` | multi_select | `{ "op": "set_multi_select", "fieldId": "cats", "selected": ["a", "b"] }` |
133
- | `set_checkboxes` | checkboxes | `{ "op": "set_checkboxes", "fieldId": "tasks", "values": {"item1": "done"} }` |
132
+ | `set_string_list` | string_list | `{ "op": "set_string_list", "fieldId": "tags", "value": ["a", "b"] }` |
133
+ | `set_single_select` | single_select | `{ "op": "set_single_select", "fieldId": "rating", "value": "high" }` |
134
+ | `set_multi_select` | multi_select | `{ "op": "set_multi_select", "fieldId": "cats", "value": ["a", "b"] }` |
135
+ | `set_checkboxes` | checkboxes | `{ "op": "set_checkboxes", "fieldId": "tasks", "value": {"item1": "done"} }` |
134
136
  | `set_url` | url | `{ "op": "set_url", "fieldId": "website", "value": "https://..." }` |
135
- | `set_url_list` | url_list | `{ "op": "set_url_list", "fieldId": "sources", "items": ["https://..."] }` |
137
+ | `set_url_list` | url_list | `{ "op": "set_url_list", "fieldId": "sources", "value": ["https://..."] }` |
136
138
  | `set_date` | date | `{ "op": "set_date", "fieldId": "deadline", "value": "2024-06-15" }` |
137
139
  | `set_year` | year | `{ "op": "set_year", "fieldId": "founded", "value": 2015 }` |
140
+ | `set_table` | table | `{ "op": "set_table", "fieldId": "data", "value": [{"col1": "v1"}] }` |
138
141
  | `clear_field` | any | `{ "op": "clear_field", "fieldId": "name" }` |
139
142
  | `skip_field` | optional | `{ "op": "skip_field", "fieldId": "notes", "reason": "Not applicable" }` |
140
143
  | `abort_field` | any | `{ "op": "abort_field", "fieldId": "data", "reason": "Unable to find" }` |
@@ -176,14 +179,90 @@ const result = await fillForm({
176
179
  form: parsedForm,
177
180
  model: 'anthropic/claude-sonnet-4-5',
178
181
  enableWebSearch: true,
182
+ captureWireFormat: false,
179
183
  targetRoles: ['agent'],
180
184
  });
181
185
  ```
182
186
 
187
+ **FillOptions fields:**
188
+
189
+ | Field | Type | Default | Description |
190
+ | --- | --- | --- | --- |
191
+ | `form` | `string \| ParsedForm` | (required) | Form markdown or parsed form |
192
+ | `model` | `string \| LanguageModel` | (required) | Model identifier or instance |
193
+ | `enableWebSearch` | `boolean` | (required) | Enable provider web search tools |
194
+ | `captureWireFormat` | `boolean` | (required) | Capture full LLM request/response |
195
+ | `inputContext` | `InputContext` | `undefined` | Pre-fill fields by ID |
196
+ | `systemPromptAddition` | `string` | `undefined` | Additional system prompt context |
197
+ | `maxTurnsTotal` | `number` | `100` | Maximum TOTAL turns across all calls (safety limit) |
198
+ | `maxTurnsThisCall` | `number` | `undefined` | Per-call turn limit for resumable fills |
199
+ | `startingTurnNumber` | `number` | `0` | Starting turn for progress tracking |
200
+ | `maxPatchesPerTurn` | `number` | `20` | Maximum patches per turn |
201
+ | `maxIssuesPerTurn` | `number` | `10` | Maximum issues shown per turn |
202
+ | `targetRoles` | `string[]` | `['agent']` | Roles to fill |
203
+ | `fillMode` | `FillMode` | `'continue'` | `'continue'` or `'overwrite'` |
204
+ | `callbacks` | `FillCallbacks` | `undefined` | Progress callbacks |
205
+ | `signal` | `AbortSignal` | `undefined` | Cancellation signal |
206
+ | `additionalTools` | `Record<string, Tool>` | `undefined` | Custom tools for agent |
207
+
208
+ ### FillStatus
209
+
210
+ The `status` field in `FillResult` indicates success or failure:
211
+
212
+ | Status | Description |
213
+ | --- | --- |
214
+ | `{ ok: true }` | Form completed successfully |
215
+ | `{ ok: false, reason: 'max_turns' }` | Hit overall `maxTurnsTotal` safety limit |
216
+ | `{ ok: false, reason: 'batch_limit' }` | Hit `maxTurnsThisCall` per-call limit |
217
+ | `{ ok: false, reason: 'cancelled' }` | Aborted via signal |
218
+ | `{ ok: false, reason: 'error' }` | Unexpected error |
219
+
220
+ ### Resumable Form Fills
221
+
222
+ For orchestrated environments with timeout constraints (e.g., Convex, AWS Step
223
+ Functions), use `maxTurnsThisCall` to limit turns per call and resume from checkpoints.
224
+
225
+ ```typescript
226
+ import { fillForm } from 'markform';
227
+
228
+ // First call - limit to 2 turns
229
+ const r1 = await fillForm({
230
+ form: formMarkdown,
231
+ model: 'anthropic/claude-sonnet-4-5',
232
+ enableWebSearch: false,
233
+ captureWireFormat: false,
234
+ maxTurnsThisCall: 2, // Stop after 2 turns
235
+ });
236
+
237
+ if (!r1.status.ok && r1.status.reason === 'batch_limit') {
238
+ // Resume from checkpoint using r1.markdown
239
+ const r2 = await fillForm({
240
+ form: r1.markdown, // Use checkpoint as input
241
+ model: 'anthropic/claude-sonnet-4-5',
242
+ enableWebSearch: false,
243
+ captureWireFormat: false,
244
+ startingTurnNumber: r1.turns, // Continue turn count
245
+ });
246
+
247
+ console.log('Total turns:', r2.turns);
248
+ console.log('Status:', r2.status);
249
+ }
250
+ ```
251
+
252
+ **Key points:**
253
+
254
+ - `maxTurnsThisCall` limits turns in a single call, returns `batch_limit` when reached
255
+
256
+ - `result.markdown` contains the form checkpoint (serialized state)
257
+
258
+ - `startingTurnNumber` ensures accurate progress tracking across calls
259
+
260
+ - The form itself is the state—no session storage needed
261
+
183
262
  ### FillCallbacks
184
263
 
185
264
  Optional callbacks for observing form-filling execution in real-time.
186
- All callbacks are optional and errors in callbacks don't abort filling.
265
+ All callbacks are optional and errors in callbacks dont abort filling.
187
266
 
188
267
  ```typescript
189
268
  import type { FillCallbacks } from 'markform';
@@ -127,10 +127,11 @@ markform:
127
127
 
128
128
  **Optional metadata fields:**
129
129
 
130
- - `run_mode` (*recommended*): Suggests how CLI tools should execute this form. Values:
131
- `interactive` (user fills via prompts), `fill` (agent fills), or `research` (agent
132
- fills with web search). When omitted, tools may infer from field roles or require
133
- explicit selection. This is a hint for tooling, not enforced by the engine.
130
+ - `run_mode` (*recommended*): Suggests how CLI tools should execute this form.
131
+ Values: `interactive` (user fills via prompts), `fill` (agent fills), or `research`
132
+ (agent fills with web search).
133
+ When omitted, tools may infer from field roles or require explicit selection.
134
+ This is a hint for tooling, not enforced by the engine.
134
135
 
135
136
  **Behavioral rules (*required*):**
136
137
 
@@ -211,8 +212,8 @@ Markform defines its own scoping rules where option IDs are field-scoped.
211
212
  Custom tags are defined following [Markdoc tag conventions][markdoc-tags]. See
212
213
  [Markdoc Config][markdoc-config] for how to register custom tags.
213
214
 
214
- All fields use the unified `{% field kind="..." %}` syntax. The `kind` attribute
215
- identifies what type of field a `Field` or `FieldValue` represents.
215
+ All fields use the unified `{% field kind="..." %}` syntax.
216
+ The `kind` attribute identifies what type of field a `Field` or `FieldValue` represents.
216
217
  (In TypeScript, the type is `FieldKind`.)
217
218
 
218
219
  | Kind | Description |
@@ -254,8 +255,9 @@ to different actors.
254
255
  | `placeholder` | string | Hint text shown in empty fields (displayed in UI) |
255
256
  | `examples` | string[] | Example values for the field (helps LLMs, shown in prompts) |
256
257
 
257
- These attributes are only valid on text-entry field kinds. Using them on chooser fields
258
- (single-select, multi-select, checkboxes) will result in a parse error.
258
+ These attributes are only valid on text-entry field kinds.
259
+ Using them on chooser fields (single-select, multi-select, checkboxes) will result in a
260
+ parse error.
259
261
 
260
262
  **Example with placeholder and examples:**
261
263
  ```md
@@ -299,7 +301,8 @@ compatibility:
299
301
  | `multi_select` | `[x]` | Selected | `- [x] Option {% #opt_id %}` |
300
302
 
301
303
  **Note:** `single_select` enforces that exactly one option has `[x]`. The distinction
302
- between `single_select` and `multi_select` is in the `kind` attribute, not the marker syntax.
304
+ between `single_select` and `multi_select` is in the `kind` attribute, not the marker
305
+ syntax.
303
306
 
304
307
  The `{% #id %}` annotation **is** native Markdoc syntax (see
305
308
  [Attributes][markdoc-attributes]).
@@ -464,8 +467,8 @@ columnTypes=[{type: "string", required: true}, "number", "url"]
464
467
  {% /field %}
465
468
  ```
466
469
 
467
- **Sentinel values in table cells:**
468
- Cells can use `%SKIP%` and `%ABORT%` sentinels with optional reasons:
470
+ **Sentinel values in table cells:** Cells can use `%SKIP%` and `%ABORT%` sentinels with
471
+ optional reasons:
469
472
  ```md
470
473
  | 2017 | I, Tonya | 90 | %SKIP% (Box office not tracked) |
471
474
  ```
@@ -906,8 +909,8 @@ ACME
906
909
  {% /field %} {% field kind="string" id="fiscal_period" label="Fiscal period"
907
910
  required=true %}{% /field %} {% /group %}
908
911
 
909
- {% group id="source_docs" title="Source Documents" %} {% checkboxes
910
- id="docs_reviewed" label="Documents reviewed" required=true %}
912
+ {% group id="source_docs" title="Source Documents" %} {% checkboxes id="docs_reviewed"
913
+ label="Documents reviewed" required=true %}
911
914
 
912
915
  - [x] 10-K {% #ten_k %}
913
916
 
@@ -915,8 +918,7 @@ id="docs_reviewed" label="Documents reviewed" required=true %}
915
918
 
916
919
  - [/] Earnings release {% #earnings_release %}
917
920
 
918
- - [ ] Earnings call transcript {% #call_transcript %} {% /field %} {% /group
919
- %}
921
+ - [ ] Earnings call transcript {% #call_transcript %} {% /field %} {% /group %}
920
922
  ````
921
923
 
922
924
  Notes:
@@ -1859,7 +1861,7 @@ YAML keys use snake_case for readability and consistency with common YAML conven
1859
1861
  | TypeScript kind | `'string_list'` |
1860
1862
  | Attributes | `id`, `label`, `required`, `minItems`, `maxItems`, `itemMinLength`, `itemMaxLength`, `uniqueItems` |
1861
1863
  | FieldValue | `{ kind: 'string_list'; items: string[] }` |
1862
- | Patch operation | `{ op: 'set_string_list'; fieldId: Id; items: string[] }` |
1864
+ | Patch operation | `{ op: 'set_string_list'; fieldId: Id; value: string[] }` |
1863
1865
  | Zod | `z.array(z.string().min(itemMin).max(itemMax)).min(n).max(m)` |
1864
1866
  | JSON Schema | `{ type: "array", items: { type: "string" }, minItems, maxItems, uniqueItems }` |
1865
1867
 
@@ -1872,7 +1874,7 @@ YAML keys use snake_case for readability and consistency with common YAML conven
1872
1874
  | TypeScript kind | `'single_select'` |
1873
1875
  | Attributes | `id`, `label`, `required` + inline `options` via list syntax |
1874
1876
  | FieldValue | `{ kind: 'single_select'; selected: OptionId \| null }` |
1875
- | Patch operation | `{ op: 'set_single_select'; fieldId: Id; selected: OptionId \| null }` |
1877
+ | Patch operation | `{ op: 'set_single_select'; fieldId: Id; value: OptionId \| null }` |
1876
1878
  | Zod | `z.enum([...optionIds])` |
1877
1879
  | JSON Schema | `{ type: "string", enum: [...optionIds] }` |
1878
1880
 
@@ -1885,7 +1887,7 @@ YAML keys use snake_case for readability and consistency with common YAML conven
1885
1887
  | TypeScript kind | `'multi_select'` |
1886
1888
  | Attributes | `id`, `label`, `required`, `minSelections`, `maxSelections` + inline `options` |
1887
1889
  | FieldValue | `{ kind: 'multi_select'; selected: OptionId[] }` |
1888
- | Patch operation | `{ op: 'set_multi_select'; fieldId: Id; selected: OptionId[] }` |
1890
+ | Patch operation | `{ op: 'set_multi_select'; fieldId: Id; value: OptionId[] }` |
1889
1891
  | Zod | `z.array(z.enum([...optionIds])).min(n).max(m)` |
1890
1892
  | JSON Schema | `{ type: "array", items: { enum: [...optionIds] }, minItems, maxItems }` |
1891
1893
 
@@ -1898,7 +1900,7 @@ YAML keys use snake_case for readability and consistency with common YAML conven
1898
1900
  | TypeScript kind | `'checkboxes'` |
1899
1901
  | Attributes | `id`, `label`, `required`, `checkboxMode` (`multi`/`simple`/`explicit`), `minDone` (simple only) + inline `options` |
1900
1902
  | FieldValue | `{ kind: 'checkboxes'; values: Record<OptionId, CheckboxValue> }` |
1901
- | Patch operation | `{ op: 'set_checkboxes'; fieldId: Id; values: Record<OptionId, CheckboxValue> }` |
1903
+ | Patch operation | `{ op: 'set_checkboxes'; fieldId: Id; value: Record<OptionId, CheckboxValue> }` |
1902
1904
  | Zod | `z.record(z.enum([...states]))` |
1903
1905
  | JSON Schema | `{ type: "object", additionalProperties: { enum: [...states] } }` |
1904
1906
 
@@ -1924,7 +1926,7 @@ YAML keys use snake_case for readability and consistency with common YAML conven
1924
1926
  | TypeScript kind | `'url_list'` |
1925
1927
  | Attributes | `id`, `label`, `required`, `minItems`, `maxItems`, `uniqueItems` |
1926
1928
  | FieldValue | `{ kind: 'url_list'; items: string[] }` |
1927
- | Patch operation | `{ op: 'set_url_list'; fieldId: Id; items: string[] }` |
1929
+ | Patch operation | `{ op: 'set_url_list'; fieldId: Id; value: string[] }` |
1928
1930
  | Zod | `z.array(z.url()).min(n).max(m)` |
1929
1931
  | JSON Schema | `{ type: "array", items: { type: "string", format: "uri" }, minItems, maxItems, uniqueItems }` |
1930
1932
 
@@ -1963,7 +1965,7 @@ YAML keys use snake_case for readability and consistency with common YAML conven
1963
1965
  | TypeScript kind | `'table'` |
1964
1966
  | Attributes | `id`, `label`, `required`, `columnIds`, `columnLabels`, `columnTypes`, `minRows`, `maxRows` |
1965
1967
  | FieldValue | `{ kind: 'table'; rows: TableRowResponse[] }` |
1966
- | Patch operation | `{ op: 'set_table'; fieldId: Id; rows: PatchTableRow[] }` |
1968
+ | Patch operation | `{ op: 'set_table'; fieldId: Id; value: PatchTableRow[] }` |
1967
1969
  | Zod | `z.object({ kind: z.literal('table'), rows: z.array(TableRowResponseSchema) })` |
1968
1970
  | JSON Schema | `{ type: "object", properties: { kind: { const: "table" }, rows: { type: "array" } } }` |
1969
1971
 
@@ -2541,12 +2543,15 @@ This formula ensures:
2541
2543
  type Patch =
2542
2544
  | { op: 'set_string'; fieldId: Id; value: string | null }
2543
2545
  | { op: 'set_number'; fieldId: Id; value: number | null }
2544
- | { op: 'set_string_list'; fieldId: Id; items: string[] }
2545
- | { op: 'set_checkboxes'; fieldId: Id; values: Record<OptionId, CheckboxValue> }
2546
- | { op: 'set_single_select'; fieldId: Id; selected: OptionId | null }
2547
- | { op: 'set_multi_select'; fieldId: Id; selected: OptionId[] }
2546
+ | { op: 'set_string_list'; fieldId: Id; value: string[] }
2547
+ | { op: 'set_checkboxes'; fieldId: Id; value: Record<OptionId, CheckboxValue> }
2548
+ | { op: 'set_single_select'; fieldId: Id; value: OptionId | null }
2549
+ | { op: 'set_multi_select'; fieldId: Id; value: OptionId[] }
2548
2550
  | { op: 'set_url'; fieldId: Id; value: string | null }
2549
- | { op: 'set_url_list'; fieldId: Id; items: string[] }
2551
+ | { op: 'set_url_list'; fieldId: Id; value: string[] }
2552
+ | { op: 'set_table'; fieldId: Id; value: PatchTableRow[] }
2553
+ | { op: 'set_date'; fieldId: Id; value: string | null }
2554
+ | { op: 'set_year'; fieldId: Id; value: number | null }
2550
2555
  | { op: 'clear_field'; fieldId: Id }
2551
2556
  | { op: 'skip_field'; fieldId: Id; role: string; reason?: string }
2552
2557
  | { op: 'abort_field'; fieldId: Id; role: string; reason?: string }
@@ -2564,10 +2569,10 @@ Option IDs in patches are **local to the field** specified by `fieldId`. You do
2564
2569
  the qualified `{fieldId}.{optionId}` form in patches—the `fieldId` already provides the
2565
2570
  scope. For example:
2566
2571
 
2567
- - `{ op: 'set_checkboxes', fieldId: 'docs_reviewed', values: { ten_k: 'done', ten_q:
2572
+ - `{ op: 'set_checkboxes', fieldId: 'docs_reviewed', value: { ten_k: 'done', ten_q:
2568
2573
  'done' } }`
2569
2574
 
2570
- - `{ op: 'set_single_select', fieldId: 'rating', selected: 'bullish' }`
2575
+ - `{ op: 'set_single_select', fieldId: 'rating', value: 'bullish' }`
2571
2576
 
2572
2577
  **Patch semantics:**
2573
2578
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markform",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Markdown forms for token-friendly workflows",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "Joshua Levy",