markform 0.1.16 → 0.1.18
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/LICENSE +369 -0
- package/README.md +154 -214
- package/dist/ai-sdk.d.mts +1 -1
- package/dist/ai-sdk.mjs +2 -2
- package/dist/{apply-CXsI5N9x.mjs → apply-BYgtU64w.mjs} +203 -16
- package/dist/apply-BYgtU64w.mjs.map +1 -0
- package/dist/bin.mjs +1 -1
- package/dist/{cli-BsFessUW.mjs → cli-D9w0Bp4J.mjs} +199 -13
- package/dist/cli-D9w0Bp4J.mjs.map +1 -0
- package/dist/cli.mjs +1 -1
- package/dist/{coreTypes-DE6Giau5.d.mts → coreTypes-BMEs8h_2.d.mts} +165 -2
- package/dist/{coreTypes-DiCddBKu.mjs → coreTypes-SDB3KRRJ.mjs} +9 -4
- package/dist/coreTypes-SDB3KRRJ.mjs.map +1 -0
- package/dist/index.d.mts +266 -2
- package/dist/index.mjs +5 -5
- package/dist/{session-B7aR6hno.mjs → session-CW9AQw6i.mjs} +1 -1
- package/dist/{session-XDrocA3j.mjs → session-Ci4B0Pna.mjs} +2 -2
- package/dist/{session-XDrocA3j.mjs.map → session-Ci4B0Pna.mjs.map} +1 -1
- package/dist/{src-Dv3IZSQU.mjs → src-DDxi-2ne.mjs} +966 -32
- package/dist/src-DDxi-2ne.mjs.map +1 -0
- package/docs/markform-apis.md +110 -0
- package/docs/markform-reference.md +58 -0
- package/docs/markform-spec.md +204 -9
- package/examples/movie-research/movie-deep-research-mock-filled.form.md +1 -1
- package/examples/movie-research/movie-deep-research.form.md +1 -1
- package/examples/parallel/parallel-research.form.md +57 -0
- package/examples/plan-document/plan-document-markdoc.form.md +35 -0
- package/examples/plan-document/plan-document-progress.form.md +47 -0
- package/examples/plan-document/plan-document.form.md +47 -0
- package/examples/startup-deep-research/startup-deep-research.form.md +1 -1
- package/package.json +2 -2
- package/dist/apply-CXsI5N9x.mjs.map +0 -1
- package/dist/cli-BsFessUW.mjs.map +0 -1
- package/dist/coreTypes-DiCddBKu.mjs.map +0 -1
- package/dist/src-Dv3IZSQU.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { L as PatchSchema, V as RunModeSchema } from "./coreTypes-
|
|
3
|
-
import {
|
|
2
|
+
import { L as PatchSchema, V as RunModeSchema } from "./coreTypes-SDB3KRRJ.mjs";
|
|
3
|
+
import { A as DEFAULT_ROLE_INSTRUCTIONS, C as DEFAULT_MAX_STEPS_PER_TURN, D as DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN, E as DEFAULT_PRIORITY, H as getWebSearchConfig, K as MarkformConfigError, O as DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN, S as DEFAULT_MAX_PATCHES_PER_TURN, Y as MarkformParseError, b as DEFAULT_MAX_ISSUES_PER_TURN, d as serializeForm, g as preprocessCommentSyntax, h as detectSyntaxStyle, i as inspect, k as DEFAULT_ROLES, r as getFieldsForRoles, t as applyPatches, v as AGENT_ROLE, w as DEFAULT_MAX_TURNS, x as DEFAULT_MAX_PARALLEL_AGENTS } from "./apply-BYgtU64w.mjs";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import Markdoc from "@markdoc/markdoc";
|
|
6
6
|
import YAML from "yaml";
|
|
@@ -588,14 +588,16 @@ function parseBaseFieldAttrs(node, kind) {
|
|
|
588
588
|
};
|
|
589
589
|
}
|
|
590
590
|
/**
|
|
591
|
-
* Get common field attributes (priority, role, validate, report).
|
|
591
|
+
* Get common field attributes (priority, role, validate, report, parallel, order).
|
|
592
592
|
*/
|
|
593
593
|
function getCommonFieldAttrs(node) {
|
|
594
594
|
return {
|
|
595
595
|
priority: getPriorityAttr(node),
|
|
596
596
|
role: getStringAttr(node, "role") ?? AGENT_ROLE,
|
|
597
597
|
validate: getValidateAttr(node),
|
|
598
|
-
report: getBooleanAttr(node, "report")
|
|
598
|
+
report: getBooleanAttr(node, "report"),
|
|
599
|
+
parallel: getStringAttr(node, "parallel"),
|
|
600
|
+
order: getNumberAttr(node, "order")
|
|
599
601
|
};
|
|
600
602
|
}
|
|
601
603
|
/**
|
|
@@ -1125,11 +1127,23 @@ const LEGACY_TAG_TO_KIND = {
|
|
|
1125
1127
|
"table-field": "table"
|
|
1126
1128
|
};
|
|
1127
1129
|
/**
|
|
1130
|
+
* Recursively check if any children of a node are field tags.
|
|
1131
|
+
* Throws MarkformParseError if a nested field is found.
|
|
1132
|
+
*/
|
|
1133
|
+
function validateNoNestedFields(node, outerFieldId) {
|
|
1134
|
+
if (!node.children || !Array.isArray(node.children)) return;
|
|
1135
|
+
for (const child of node.children) {
|
|
1136
|
+
if (isTagNode(child, "field")) throw new MarkformParseError(`Field tags cannot be nested. Found '${getStringAttr(child, "id") ?? "unknown"}' inside '${outerFieldId}'`);
|
|
1137
|
+
validateNoNestedFields(child, outerFieldId);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1128
1141
|
* Parse a unified field tag: {% field kind="..." ... %}
|
|
1129
1142
|
*/
|
|
1130
1143
|
function parseUnifiedField(node) {
|
|
1131
1144
|
const kind = getStringAttr(node, "kind");
|
|
1132
1145
|
if (!kind) throw new MarkformParseError("field tag missing required 'kind' attribute");
|
|
1146
|
+
validateNoNestedFields(node, getStringAttr(node, "id") ?? "unknown");
|
|
1133
1147
|
if (!FIELD_KINDS.includes(kind)) throw new MarkformParseError(`field tag has invalid kind '${kind}'. Valid kinds: ${FIELD_KINDS.join(", ")}`);
|
|
1134
1148
|
switch (kind) {
|
|
1135
1149
|
case "string": return parseStringField(node);
|
|
@@ -1159,6 +1173,274 @@ function parseField(node) {
|
|
|
1159
1173
|
return null;
|
|
1160
1174
|
}
|
|
1161
1175
|
|
|
1176
|
+
//#endregion
|
|
1177
|
+
//#region src/markdown/markdownHeaders.ts
|
|
1178
|
+
const ATX_HEADING_PATTERN = /^(#{1,6})\s+(.*)$/;
|
|
1179
|
+
/**
|
|
1180
|
+
* Find all headings in a markdown document.
|
|
1181
|
+
* Returns headings in document order.
|
|
1182
|
+
*
|
|
1183
|
+
* Only ATX-style headings (# Title) are recognized.
|
|
1184
|
+
* Headings inside fenced code blocks are ignored.
|
|
1185
|
+
*/
|
|
1186
|
+
function findAllHeadings(markdown) {
|
|
1187
|
+
const lines = markdown.split("\n");
|
|
1188
|
+
const headings = [];
|
|
1189
|
+
let inCodeBlock = false;
|
|
1190
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1191
|
+
const line = lines[i] ?? "";
|
|
1192
|
+
const lineNumber = i + 1;
|
|
1193
|
+
if (line.startsWith("```") || line.startsWith("~~~")) {
|
|
1194
|
+
inCodeBlock = !inCodeBlock;
|
|
1195
|
+
continue;
|
|
1196
|
+
}
|
|
1197
|
+
if (inCodeBlock) continue;
|
|
1198
|
+
const match = ATX_HEADING_PATTERN.exec(line);
|
|
1199
|
+
if (match) {
|
|
1200
|
+
const hashes = match[1] ?? "";
|
|
1201
|
+
const title = (match[2] ?? "").trim();
|
|
1202
|
+
headings.push({
|
|
1203
|
+
level: hashes.length,
|
|
1204
|
+
title,
|
|
1205
|
+
line: lineNumber,
|
|
1206
|
+
position: {
|
|
1207
|
+
start: {
|
|
1208
|
+
line: lineNumber,
|
|
1209
|
+
col: 1
|
|
1210
|
+
},
|
|
1211
|
+
end: {
|
|
1212
|
+
line: lineNumber,
|
|
1213
|
+
col: line.length + 1
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
return headings;
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* Find all headings that enclose a given line position.
|
|
1223
|
+
* Returns headings from innermost (most specific) to outermost (least specific).
|
|
1224
|
+
*
|
|
1225
|
+
* A heading "encloses" a line if:
|
|
1226
|
+
* 1. The heading appears before the line
|
|
1227
|
+
* 2. No heading of equal or higher level appears between them
|
|
1228
|
+
*
|
|
1229
|
+
* @param markdown - The markdown source text
|
|
1230
|
+
* @param line - The line number (1-indexed)
|
|
1231
|
+
* @returns Array of enclosing headings, innermost first
|
|
1232
|
+
*/
|
|
1233
|
+
function findEnclosingHeadings(markdown, line) {
|
|
1234
|
+
if (line <= 0) return [];
|
|
1235
|
+
const precedingHeadings = findAllHeadings(markdown).filter((h) => h.line < line);
|
|
1236
|
+
if (precedingHeadings.length === 0) return [];
|
|
1237
|
+
const result = [];
|
|
1238
|
+
let minLevelSeen = Infinity;
|
|
1239
|
+
for (let i = precedingHeadings.length - 1; i >= 0; i--) {
|
|
1240
|
+
const heading = precedingHeadings[i];
|
|
1241
|
+
if (!heading) continue;
|
|
1242
|
+
if (heading.level < minLevelSeen) {
|
|
1243
|
+
result.push(heading);
|
|
1244
|
+
minLevelSeen = heading.level;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
return result;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
//#endregion
|
|
1251
|
+
//#region src/engine/injectIds.ts
|
|
1252
|
+
const CHECKBOX_PATTERN = /^(\s*)-\s+(\[[^\]]\])\s+(.*)$/;
|
|
1253
|
+
const MARKDOC_ID_PATTERN = /\{%\s*#(\w+)\s*%\}/;
|
|
1254
|
+
const HTML_COMMENT_ID_PATTERN = /<!--\s*#(\w+)\s*-->/;
|
|
1255
|
+
/**
|
|
1256
|
+
* Find all checkboxes in a markdown document.
|
|
1257
|
+
* Returns checkboxes in document order with enclosing heading info.
|
|
1258
|
+
*/
|
|
1259
|
+
function findAllCheckboxes(markdown) {
|
|
1260
|
+
const lines = markdown.split("\n");
|
|
1261
|
+
const checkboxes = [];
|
|
1262
|
+
let inCodeBlock = false;
|
|
1263
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1264
|
+
const line = lines[i] ?? "";
|
|
1265
|
+
const lineNumber = i + 1;
|
|
1266
|
+
if (line.trimStart().startsWith("```") || line.trimStart().startsWith("~~~")) {
|
|
1267
|
+
inCodeBlock = !inCodeBlock;
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
if (inCodeBlock) continue;
|
|
1271
|
+
const match = CHECKBOX_PATTERN.exec(line);
|
|
1272
|
+
if (!match) continue;
|
|
1273
|
+
const marker$2 = match[2] ?? "";
|
|
1274
|
+
const rest = match[3] ?? "";
|
|
1275
|
+
const state = CHECKBOX_MARKERS[marker$2];
|
|
1276
|
+
if (state === void 0) continue;
|
|
1277
|
+
let label = rest;
|
|
1278
|
+
let id;
|
|
1279
|
+
const markdocMatch = MARKDOC_ID_PATTERN.exec(rest);
|
|
1280
|
+
if (markdocMatch) {
|
|
1281
|
+
id = markdocMatch[1];
|
|
1282
|
+
label = rest.replace(MARKDOC_ID_PATTERN, "").trim();
|
|
1283
|
+
} else {
|
|
1284
|
+
const htmlMatch = HTML_COMMENT_ID_PATTERN.exec(rest);
|
|
1285
|
+
if (htmlMatch) {
|
|
1286
|
+
id = htmlMatch[1];
|
|
1287
|
+
label = rest.replace(HTML_COMMENT_ID_PATTERN, "").trim();
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
const enclosingHeadings = findEnclosingHeadings(markdown, lineNumber);
|
|
1291
|
+
checkboxes.push({
|
|
1292
|
+
id,
|
|
1293
|
+
label,
|
|
1294
|
+
state,
|
|
1295
|
+
line: lineNumber,
|
|
1296
|
+
position: {
|
|
1297
|
+
start: {
|
|
1298
|
+
line: lineNumber,
|
|
1299
|
+
col: 1
|
|
1300
|
+
},
|
|
1301
|
+
end: {
|
|
1302
|
+
line: lineNumber,
|
|
1303
|
+
col: line.length + 1
|
|
1304
|
+
}
|
|
1305
|
+
},
|
|
1306
|
+
enclosingHeadings
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
return checkboxes;
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Inject IDs into checkboxes in a markdown document.
|
|
1313
|
+
*
|
|
1314
|
+
* Uses a generator function to create unique IDs for each checkbox.
|
|
1315
|
+
* Throws if duplicate IDs are generated or if generated IDs conflict
|
|
1316
|
+
* with existing ones.
|
|
1317
|
+
*/
|
|
1318
|
+
function injectCheckboxIds(markdown, options) {
|
|
1319
|
+
const { generator, onlyMissing = true } = options;
|
|
1320
|
+
const checkboxes = findAllCheckboxes(markdown);
|
|
1321
|
+
const existingIds = /* @__PURE__ */ new Set();
|
|
1322
|
+
for (const checkbox of checkboxes) if (checkbox.id && onlyMissing) existingIds.add(checkbox.id);
|
|
1323
|
+
const needsId = checkboxes.filter((cb) => onlyMissing ? !cb.id : true);
|
|
1324
|
+
const generatedIds = /* @__PURE__ */ new Map();
|
|
1325
|
+
const allGeneratedIds = /* @__PURE__ */ new Set();
|
|
1326
|
+
for (let i = 0; i < needsId.length; i++) {
|
|
1327
|
+
const checkbox = needsId[i];
|
|
1328
|
+
const newId = generator(checkbox, i);
|
|
1329
|
+
if (allGeneratedIds.has(newId)) throw new MarkformParseError(`Duplicate generated ID '${newId}' for checkbox '${checkbox.label}'`, { line: checkbox.line });
|
|
1330
|
+
if (onlyMissing && existingIds.has(newId)) throw new MarkformParseError(`Generated ID '${newId}' conflicts with existing ID`, { line: checkbox.line });
|
|
1331
|
+
allGeneratedIds.add(newId);
|
|
1332
|
+
generatedIds.set(checkbox.label, newId);
|
|
1333
|
+
}
|
|
1334
|
+
if (needsId.length === 0) return {
|
|
1335
|
+
markdown,
|
|
1336
|
+
injectedCount: 0,
|
|
1337
|
+
injectedIds: /* @__PURE__ */ new Map()
|
|
1338
|
+
};
|
|
1339
|
+
const lines = markdown.split("\n");
|
|
1340
|
+
const sortedByLine = [...needsId].sort((a, b) => b.line - a.line);
|
|
1341
|
+
for (const checkbox of sortedByLine) {
|
|
1342
|
+
const lineIndex = checkbox.line - 1;
|
|
1343
|
+
const line = lines[lineIndex];
|
|
1344
|
+
const newId = generatedIds.get(checkbox.label);
|
|
1345
|
+
let updatedLine = line;
|
|
1346
|
+
if (!onlyMissing || checkbox.id) {
|
|
1347
|
+
updatedLine = updatedLine.replace(MARKDOC_ID_PATTERN, "").trim();
|
|
1348
|
+
updatedLine = updatedLine.replace(HTML_COMMENT_ID_PATTERN, "").trim();
|
|
1349
|
+
}
|
|
1350
|
+
lines[lineIndex] = `${updatedLine} {% #${newId} %}`;
|
|
1351
|
+
}
|
|
1352
|
+
return {
|
|
1353
|
+
markdown: lines.join("\n"),
|
|
1354
|
+
injectedCount: needsId.length,
|
|
1355
|
+
injectedIds: generatedIds
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Find all headings with their existing IDs.
|
|
1360
|
+
* Strips ID annotations from title for clean generator input.
|
|
1361
|
+
*/
|
|
1362
|
+
function findAllHeadingsWithIds(markdown) {
|
|
1363
|
+
const headings = findAllHeadings(markdown);
|
|
1364
|
+
const lines = markdown.split("\n");
|
|
1365
|
+
return headings.map((heading) => {
|
|
1366
|
+
const line = lines[heading.line - 1] ?? "";
|
|
1367
|
+
let id;
|
|
1368
|
+
let cleanTitle = heading.title;
|
|
1369
|
+
const markdocMatch = MARKDOC_ID_PATTERN.exec(line);
|
|
1370
|
+
if (markdocMatch) {
|
|
1371
|
+
id = markdocMatch[1];
|
|
1372
|
+
cleanTitle = heading.title.replace(MARKDOC_ID_PATTERN, "").trim();
|
|
1373
|
+
} else {
|
|
1374
|
+
const htmlMatch = HTML_COMMENT_ID_PATTERN.exec(line);
|
|
1375
|
+
if (htmlMatch) {
|
|
1376
|
+
id = htmlMatch[1];
|
|
1377
|
+
cleanTitle = heading.title.replace(HTML_COMMENT_ID_PATTERN, "").trim();
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
return {
|
|
1381
|
+
...heading,
|
|
1382
|
+
title: cleanTitle,
|
|
1383
|
+
id
|
|
1384
|
+
};
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Inject IDs into headings in a markdown document.
|
|
1389
|
+
*
|
|
1390
|
+
* Uses a generator function to create unique IDs for each heading.
|
|
1391
|
+
* Throws if duplicate IDs are generated or if generated IDs conflict
|
|
1392
|
+
* with existing ones.
|
|
1393
|
+
*/
|
|
1394
|
+
function injectHeaderIds(markdown, options) {
|
|
1395
|
+
const { generator, onlyMissing = true, levels = [
|
|
1396
|
+
1,
|
|
1397
|
+
2,
|
|
1398
|
+
3,
|
|
1399
|
+
4,
|
|
1400
|
+
5,
|
|
1401
|
+
6
|
|
1402
|
+
] } = options;
|
|
1403
|
+
const allHeadings = findAllHeadingsWithIds(markdown);
|
|
1404
|
+
const levelSet = new Set(levels);
|
|
1405
|
+
const headings = allHeadings.filter((h) => levelSet.has(h.level));
|
|
1406
|
+
const existingIds = /* @__PURE__ */ new Set();
|
|
1407
|
+
for (const heading of headings) if (heading.id && onlyMissing) existingIds.add(heading.id);
|
|
1408
|
+
const needsId = headings.filter((h) => onlyMissing ? !h.id : true);
|
|
1409
|
+
const generatedIds = /* @__PURE__ */ new Map();
|
|
1410
|
+
const allGeneratedIds = /* @__PURE__ */ new Set();
|
|
1411
|
+
for (let i = 0; i < needsId.length; i++) {
|
|
1412
|
+
const heading = needsId[i];
|
|
1413
|
+
const newId = generator(heading, i);
|
|
1414
|
+
if (allGeneratedIds.has(newId)) throw new MarkformParseError(`Duplicate generated ID '${newId}' for heading '${heading.title}'`, { line: heading.line });
|
|
1415
|
+
if (onlyMissing && existingIds.has(newId)) throw new MarkformParseError(`Generated ID '${newId}' conflicts with existing ID`, { line: heading.line });
|
|
1416
|
+
allGeneratedIds.add(newId);
|
|
1417
|
+
generatedIds.set(heading.title, newId);
|
|
1418
|
+
}
|
|
1419
|
+
if (needsId.length === 0) return {
|
|
1420
|
+
markdown,
|
|
1421
|
+
injectedCount: 0,
|
|
1422
|
+
injectedIds: /* @__PURE__ */ new Map()
|
|
1423
|
+
};
|
|
1424
|
+
const lines = markdown.split("\n");
|
|
1425
|
+
const sortedByLine = [...needsId].sort((a, b) => b.line - a.line);
|
|
1426
|
+
for (const heading of sortedByLine) {
|
|
1427
|
+
const lineIndex = heading.line - 1;
|
|
1428
|
+
const line = lines[lineIndex];
|
|
1429
|
+
const newId = generatedIds.get(heading.title);
|
|
1430
|
+
let updatedLine = line;
|
|
1431
|
+
if (!onlyMissing || heading.id) {
|
|
1432
|
+
updatedLine = updatedLine.replace(MARKDOC_ID_PATTERN, "").trim();
|
|
1433
|
+
updatedLine = updatedLine.replace(HTML_COMMENT_ID_PATTERN, "").trim();
|
|
1434
|
+
}
|
|
1435
|
+
lines[lineIndex] = `${updatedLine} {% #${newId} %}`;
|
|
1436
|
+
}
|
|
1437
|
+
return {
|
|
1438
|
+
markdown: lines.join("\n"),
|
|
1439
|
+
injectedCount: needsId.length,
|
|
1440
|
+
injectedIds: generatedIds
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1162
1444
|
//#endregion
|
|
1163
1445
|
//#region src/engine/parse.ts
|
|
1164
1446
|
/**
|
|
@@ -1180,7 +1462,8 @@ const VALID_FORM_TAGS = new Set([
|
|
|
1180
1462
|
]);
|
|
1181
1463
|
/**
|
|
1182
1464
|
* Parse harness configuration from frontmatter.
|
|
1183
|
-
*
|
|
1465
|
+
* YAML keys must be snake_case; they are mapped to camelCase internally.
|
|
1466
|
+
* Unrecognized keys produce a parse error.
|
|
1184
1467
|
*/
|
|
1185
1468
|
function parseHarnessConfig(raw) {
|
|
1186
1469
|
if (!raw || typeof raw !== "object") return;
|
|
@@ -1188,15 +1471,15 @@ function parseHarnessConfig(raw) {
|
|
|
1188
1471
|
const result = {};
|
|
1189
1472
|
const keyMap = {
|
|
1190
1473
|
max_turns: "maxTurns",
|
|
1191
|
-
maxTurns: "maxTurns",
|
|
1192
1474
|
max_patches_per_turn: "maxPatchesPerTurn",
|
|
1193
|
-
maxPatchesPerTurn: "maxPatchesPerTurn",
|
|
1194
1475
|
max_issues_per_turn: "maxIssuesPerTurn",
|
|
1195
|
-
|
|
1476
|
+
max_parallel_agents: "maxParallelAgents"
|
|
1196
1477
|
};
|
|
1197
1478
|
for (const [key, value] of Object.entries(config)) {
|
|
1198
1479
|
const camelKey = keyMap[key];
|
|
1199
|
-
if (camelKey
|
|
1480
|
+
if (!camelKey) throw new MarkformParseError(`Unknown harness config key '${key}'. Valid keys: ${Object.keys(keyMap).join(", ")}`);
|
|
1481
|
+
if (typeof value !== "number") throw new MarkformParseError(`Harness config key '${key}' must be a number, got ${typeof value}`);
|
|
1482
|
+
result[camelKey] = value;
|
|
1200
1483
|
}
|
|
1201
1484
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
1202
1485
|
}
|
|
@@ -1237,6 +1520,100 @@ function extractFrontmatter(ast) {
|
|
|
1237
1520
|
}
|
|
1238
1521
|
}
|
|
1239
1522
|
/**
|
|
1523
|
+
* Extract the raw source content minus frontmatter.
|
|
1524
|
+
* This is the content that will be used for splice-based serialization.
|
|
1525
|
+
*/
|
|
1526
|
+
function extractRawSource(preprocessed) {
|
|
1527
|
+
const frontmatterMatch = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/.exec(preprocessed);
|
|
1528
|
+
if (frontmatterMatch) return preprocessed.slice(frontmatterMatch[0].length);
|
|
1529
|
+
return preprocessed;
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Build a line-to-offset mapping for converting Markdoc line numbers to byte offsets.
|
|
1533
|
+
* Returns an array where lineOffsets[lineNumber] = byte offset of line start.
|
|
1534
|
+
* Line numbers are 0-indexed (Markdoc uses 0-indexed lines).
|
|
1535
|
+
*/
|
|
1536
|
+
function buildLineOffsets(source) {
|
|
1537
|
+
const offsets = [0];
|
|
1538
|
+
for (let i = 0; i < source.length; i++) if (source[i] === "\n") offsets.push(i + 1);
|
|
1539
|
+
return offsets;
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Extract tag region from a Markdoc AST node.
|
|
1543
|
+
* Uses the node's location property (line numbers) and converts to byte offsets.
|
|
1544
|
+
*/
|
|
1545
|
+
function extractTagRegion(node, tagType, tagId, lineOffsets, frontmatterLineCount, sourceLength, hasValue) {
|
|
1546
|
+
const location = node.location;
|
|
1547
|
+
if (!location?.start || !location?.end) return null;
|
|
1548
|
+
const startLine = location.start.line - frontmatterLineCount;
|
|
1549
|
+
const endLine = location.end.line - frontmatterLineCount;
|
|
1550
|
+
if (startLine < 0 || endLine < 0) return null;
|
|
1551
|
+
const startOffset = lineOffsets[startLine] ?? 0;
|
|
1552
|
+
let endOffset;
|
|
1553
|
+
if (endLine + 1 < lineOffsets.length) endOffset = lineOffsets[endLine + 1] ?? sourceLength;
|
|
1554
|
+
else endOffset = sourceLength;
|
|
1555
|
+
if (startOffset >= endOffset || startOffset < 0 || endOffset > sourceLength) return null;
|
|
1556
|
+
return {
|
|
1557
|
+
tagId,
|
|
1558
|
+
tagType,
|
|
1559
|
+
startOffset,
|
|
1560
|
+
endOffset,
|
|
1561
|
+
...hasValue !== void 0 && { includesValue: hasValue }
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* Collect all tag regions from the AST for content preservation.
|
|
1566
|
+
* Traverses the AST and extracts positions of all Markform tags.
|
|
1567
|
+
*/
|
|
1568
|
+
function collectTagRegions(ast, rawSource, frontmatterLineCount, responsesByFieldId) {
|
|
1569
|
+
const regions = [];
|
|
1570
|
+
const lineOffsets = buildLineOffsets(rawSource);
|
|
1571
|
+
const sourceLength = rawSource.length;
|
|
1572
|
+
function traverse(node) {
|
|
1573
|
+
if (!node || typeof node !== "object") return;
|
|
1574
|
+
if (isTagNode(node, "form")) {
|
|
1575
|
+
const id = getStringAttr(node, "id");
|
|
1576
|
+
if (id) {
|
|
1577
|
+
const region = extractTagRegion(node, "form", id, lineOffsets, frontmatterLineCount, sourceLength);
|
|
1578
|
+
if (region) regions.push(region);
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
if (isTagNode(node, "group")) {
|
|
1582
|
+
const id = getStringAttr(node, "id");
|
|
1583
|
+
if (id) {
|
|
1584
|
+
const region = extractTagRegion(node, "group", id, lineOffsets, frontmatterLineCount, sourceLength);
|
|
1585
|
+
if (region) regions.push(region);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
if (isTagNode(node, "field")) {
|
|
1589
|
+
const id = getStringAttr(node, "id");
|
|
1590
|
+
if (id) {
|
|
1591
|
+
const region = extractTagRegion(node, "field", id, lineOffsets, frontmatterLineCount, sourceLength, responsesByFieldId[id]?.value !== void 0);
|
|
1592
|
+
if (region) regions.push(region);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
if (isTagNode(node, "note")) {
|
|
1596
|
+
const id = getStringAttr(node, "id");
|
|
1597
|
+
if (id) {
|
|
1598
|
+
const region = extractTagRegion(node, "note", id, lineOffsets, frontmatterLineCount, sourceLength);
|
|
1599
|
+
if (region) regions.push(region);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
if (node.type === "tag" && node.tag && [
|
|
1603
|
+
"instructions",
|
|
1604
|
+
"description",
|
|
1605
|
+
"documentation"
|
|
1606
|
+
].includes(node.tag)) {
|
|
1607
|
+
const region = extractTagRegion(node, "documentation", getStringAttr(node, "ref") ?? `doc_${regions.length}`, lineOffsets, frontmatterLineCount, sourceLength);
|
|
1608
|
+
if (region) regions.push(region);
|
|
1609
|
+
}
|
|
1610
|
+
if (node.children && Array.isArray(node.children)) for (const child of node.children) traverse(child);
|
|
1611
|
+
}
|
|
1612
|
+
traverse(ast);
|
|
1613
|
+
regions.sort((a, b) => a.startOffset - b.startOffset);
|
|
1614
|
+
return regions;
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1240
1617
|
* Parse a group tag.
|
|
1241
1618
|
*/
|
|
1242
1619
|
function parseFieldGroup(node, responsesByFieldId, orderIndex, idIndex, parentId) {
|
|
@@ -1275,19 +1652,31 @@ function parseFieldGroup(node, responsesByFieldId, orderIndex, idIndex, parentId
|
|
|
1275
1652
|
if (child.children && Array.isArray(child.children)) for (const c of child.children) processChildren(c);
|
|
1276
1653
|
}
|
|
1277
1654
|
if (node.children && Array.isArray(node.children)) for (const child of node.children) processChildren(child);
|
|
1655
|
+
const parallel = getStringAttr(node, "parallel");
|
|
1656
|
+
const order = getNumberAttr(node, "order");
|
|
1657
|
+
const groupEffectiveOrder = order ?? 0;
|
|
1658
|
+
for (const child of children) {
|
|
1659
|
+
if (child.parallel) throw new MarkformParseError(`Field '${child.id}' has parallel='${child.parallel}' but is inside group '${id}'. The parallel attribute is only allowed on top-level fields and groups.`);
|
|
1660
|
+
if (child.order !== void 0) {
|
|
1661
|
+
if (child.order !== groupEffectiveOrder) throw new MarkformParseError(`Field '${child.id}' has order=${child.order} but is inside group '${id}' with order=${groupEffectiveOrder}. A field inside a group must not specify a different order.`);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1278
1664
|
return {
|
|
1279
1665
|
id,
|
|
1280
1666
|
title,
|
|
1281
1667
|
validate: getValidateAttr(node),
|
|
1282
1668
|
children,
|
|
1283
|
-
report: getBooleanAttr(node, "report")
|
|
1669
|
+
report: getBooleanAttr(node, "report"),
|
|
1670
|
+
parallel,
|
|
1671
|
+
order
|
|
1284
1672
|
};
|
|
1285
1673
|
}
|
|
1286
1674
|
/**
|
|
1287
1675
|
* Parse a form tag.
|
|
1288
1676
|
* Handles both explicit groups and fields placed directly under the form.
|
|
1677
|
+
* Also handles implicit checkboxes when form has no explicit fields.
|
|
1289
1678
|
*/
|
|
1290
|
-
function parseFormTag(node, responsesByFieldId, orderIndex, idIndex) {
|
|
1679
|
+
function parseFormTag(node, responsesByFieldId, orderIndex, idIndex, markdown) {
|
|
1291
1680
|
const id = getStringAttr(node, "id");
|
|
1292
1681
|
const title = getStringAttr(node, "title");
|
|
1293
1682
|
if (!id) throw new MarkformParseError("form missing required 'id' attribute");
|
|
@@ -1328,18 +1717,89 @@ function parseFormTag(node, responsesByFieldId, orderIndex, idIndex) {
|
|
|
1328
1717
|
}
|
|
1329
1718
|
if (node.children && Array.isArray(node.children)) for (const child of node.children) processContent(child);
|
|
1330
1719
|
if (ungroupedFields.length > 0) {
|
|
1331
|
-
const implicitGroupId =
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1720
|
+
const implicitGroupId = "default";
|
|
1721
|
+
const existingDefault = groups.find((g) => g.id === implicitGroupId);
|
|
1722
|
+
if (existingDefault) existingDefault.children = [...existingDefault.children ?? [], ...ungroupedFields];
|
|
1723
|
+
else {
|
|
1724
|
+
idIndex.set(implicitGroupId, {
|
|
1725
|
+
nodeType: "group",
|
|
1726
|
+
parentId: id
|
|
1727
|
+
});
|
|
1728
|
+
groups.push({
|
|
1729
|
+
id: implicitGroupId,
|
|
1730
|
+
children: ungroupedFields,
|
|
1731
|
+
implicit: true
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
const hasExplicitFields = ungroupedFields.length > 0 || groups.some((g) => g.children && g.children.length > 0);
|
|
1736
|
+
const hasExplicitCheckboxStyleFields = groups.some((g) => g.children?.some((f) => f.kind === "checkboxes" || f.kind === "single_select" || f.kind === "multi_select"));
|
|
1737
|
+
const allCheckboxes = findAllCheckboxes(markdown);
|
|
1738
|
+
if (allCheckboxes.length > 0) if (hasExplicitFields) {
|
|
1739
|
+
if (!hasExplicitCheckboxStyleFields) throw new MarkformParseError("Checkboxes found outside of field tags. Either wrap all checkboxes in fields or remove all explicit fields for implicit checkboxes mode.");
|
|
1740
|
+
} else {
|
|
1741
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
1742
|
+
const options = [];
|
|
1743
|
+
const values = {};
|
|
1744
|
+
for (const checkbox of allCheckboxes) {
|
|
1745
|
+
if (!checkbox.id) throw new MarkformParseError(`Option in implicit field 'checkboxes' missing ID annotation. Use {% #option_id %}`, { line: checkbox.line });
|
|
1746
|
+
if (seenIds.has(checkbox.id)) throw new MarkformParseError(`Duplicate option ID '${checkbox.id}' in field 'checkboxes'`, { line: checkbox.line });
|
|
1747
|
+
seenIds.add(checkbox.id);
|
|
1748
|
+
options.push({
|
|
1749
|
+
id: checkbox.id,
|
|
1750
|
+
label: checkbox.label
|
|
1751
|
+
});
|
|
1752
|
+
values[checkbox.id] = checkbox.state;
|
|
1753
|
+
}
|
|
1754
|
+
const implicitField = {
|
|
1755
|
+
kind: "checkboxes",
|
|
1756
|
+
id: "checkboxes",
|
|
1757
|
+
label: "Checkboxes",
|
|
1758
|
+
checkboxMode: "multi",
|
|
1759
|
+
implicit: true,
|
|
1760
|
+
options,
|
|
1761
|
+
required: false,
|
|
1762
|
+
priority: DEFAULT_PRIORITY,
|
|
1763
|
+
role: AGENT_ROLE,
|
|
1764
|
+
approvalMode: "none"
|
|
1765
|
+
};
|
|
1766
|
+
idIndex.set("checkboxes", {
|
|
1767
|
+
nodeType: "field",
|
|
1335
1768
|
parentId: id
|
|
1336
1769
|
});
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1770
|
+
orderIndex.push("checkboxes");
|
|
1771
|
+
for (const opt of options) {
|
|
1772
|
+
const qualifiedRef = `checkboxes.${opt.id}`;
|
|
1773
|
+
idIndex.set(qualifiedRef, {
|
|
1774
|
+
nodeType: "option",
|
|
1775
|
+
parentId: id,
|
|
1776
|
+
fieldId: "checkboxes"
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
responsesByFieldId.checkboxes = {
|
|
1780
|
+
state: "answered",
|
|
1781
|
+
value: {
|
|
1782
|
+
kind: "checkboxes",
|
|
1783
|
+
values
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
let defaultGroup = groups.find((g) => g.id === "default");
|
|
1787
|
+
if (!defaultGroup) {
|
|
1788
|
+
defaultGroup = {
|
|
1789
|
+
id: "default",
|
|
1790
|
+
children: [],
|
|
1791
|
+
implicit: true
|
|
1792
|
+
};
|
|
1793
|
+
idIndex.set("default", {
|
|
1794
|
+
nodeType: "group",
|
|
1795
|
+
parentId: id
|
|
1796
|
+
});
|
|
1797
|
+
groups.push(defaultGroup);
|
|
1798
|
+
}
|
|
1799
|
+
defaultGroup.children = defaultGroup.children || [];
|
|
1800
|
+
defaultGroup.children.push(implicitField);
|
|
1342
1801
|
}
|
|
1802
|
+
validateParallelBatches(groups);
|
|
1343
1803
|
return {
|
|
1344
1804
|
id,
|
|
1345
1805
|
title,
|
|
@@ -1347,6 +1807,39 @@ function parseFormTag(node, responsesByFieldId, orderIndex, idIndex) {
|
|
|
1347
1807
|
};
|
|
1348
1808
|
}
|
|
1349
1809
|
/**
|
|
1810
|
+
* Validate that parallel batches have consistent order and role values.
|
|
1811
|
+
*/
|
|
1812
|
+
function validateParallelBatches(groups) {
|
|
1813
|
+
const batches = /* @__PURE__ */ new Map();
|
|
1814
|
+
for (const group of groups) if (group.implicit) {
|
|
1815
|
+
for (const field of group.children) if (field.parallel) {
|
|
1816
|
+
const list = batches.get(field.parallel) ?? [];
|
|
1817
|
+
list.push({
|
|
1818
|
+
order: field.order ?? 0,
|
|
1819
|
+
role: field.role,
|
|
1820
|
+
itemId: field.id
|
|
1821
|
+
});
|
|
1822
|
+
batches.set(field.parallel, list);
|
|
1823
|
+
}
|
|
1824
|
+
} else if (group.parallel) {
|
|
1825
|
+
const groupRole = group.children[0]?.role ?? AGENT_ROLE;
|
|
1826
|
+
const list = batches.get(group.parallel) ?? [];
|
|
1827
|
+
list.push({
|
|
1828
|
+
order: group.order ?? 0,
|
|
1829
|
+
role: groupRole,
|
|
1830
|
+
itemId: group.id
|
|
1831
|
+
});
|
|
1832
|
+
batches.set(group.parallel, list);
|
|
1833
|
+
}
|
|
1834
|
+
for (const [batchId, items] of batches) {
|
|
1835
|
+
if (items.length < 2) continue;
|
|
1836
|
+
const firstOrder = items[0].order;
|
|
1837
|
+
const firstRole = items[0].role;
|
|
1838
|
+
if (items.filter((i) => i.order !== firstOrder).length > 0) throw new MarkformParseError(`Parallel batch '${batchId}' has items with different order values (${[...new Set(items.map((i) => i.order))].join(", ")}). All items in a parallel batch must have the same order.`);
|
|
1839
|
+
if (items.filter((i) => i.role !== firstRole).length > 0) throw new MarkformParseError(`Parallel batch '${batchId}' has items with different roles (${[...new Set(items.map((i) => i.role))].join(", ")}). All items in a parallel batch must have the same role.`);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
/**
|
|
1350
1843
|
* Extract all notes from AST.
|
|
1351
1844
|
* Looks for {% note %} tags with id, ref, role, and optional state.
|
|
1352
1845
|
*/
|
|
@@ -1441,6 +1934,8 @@ function extractDocBlocks(ast, idIndex) {
|
|
|
1441
1934
|
function parseForm(markdown) {
|
|
1442
1935
|
const syntaxStyle = detectSyntaxStyle(markdown);
|
|
1443
1936
|
const preprocessed = preprocessCommentSyntax(markdown);
|
|
1937
|
+
const rawSource = extractRawSource(preprocessed);
|
|
1938
|
+
const frontmatterLineCount = (preprocessed.slice(0, preprocessed.length - rawSource.length).match(/\n/g) ?? []).length;
|
|
1444
1939
|
const ast = Markdoc.parse(preprocessed);
|
|
1445
1940
|
const { metadata, description } = extractFrontmatter(ast);
|
|
1446
1941
|
let formSchema = null;
|
|
@@ -1451,25 +1946,31 @@ function parseForm(markdown) {
|
|
|
1451
1946
|
if (!node || typeof node !== "object") return;
|
|
1452
1947
|
if (isTagNode(node, "form")) {
|
|
1453
1948
|
if (formSchema) throw new MarkformParseError("Multiple form tags found - only one allowed");
|
|
1454
|
-
formSchema = parseFormTag(node, responsesByFieldId, orderIndex, idIndex);
|
|
1949
|
+
formSchema = parseFormTag(node, responsesByFieldId, orderIndex, idIndex, preprocessed);
|
|
1455
1950
|
return;
|
|
1456
1951
|
}
|
|
1457
1952
|
if (node.children && Array.isArray(node.children)) for (const child of node.children) findFormTag(child);
|
|
1458
1953
|
}
|
|
1459
1954
|
findFormTag(ast);
|
|
1460
1955
|
if (!formSchema) throw new MarkformParseError("No form tag found in document");
|
|
1956
|
+
const schema = {
|
|
1957
|
+
...formSchema,
|
|
1958
|
+
...description && { description }
|
|
1959
|
+
};
|
|
1960
|
+
const notes = extractNotes(ast, idIndex);
|
|
1961
|
+
const docs = extractDocBlocks(ast, idIndex);
|
|
1962
|
+
const tagRegions = collectTagRegions(ast, rawSource, frontmatterLineCount, responsesByFieldId);
|
|
1461
1963
|
return {
|
|
1462
|
-
schema
|
|
1463
|
-
...formSchema,
|
|
1464
|
-
...description && { description }
|
|
1465
|
-
},
|
|
1964
|
+
schema,
|
|
1466
1965
|
responsesByFieldId,
|
|
1467
|
-
notes
|
|
1468
|
-
docs
|
|
1966
|
+
notes,
|
|
1967
|
+
docs,
|
|
1469
1968
|
orderIndex,
|
|
1470
1969
|
idIndex,
|
|
1471
1970
|
...metadata && { metadata },
|
|
1472
|
-
syntaxStyle
|
|
1971
|
+
syntaxStyle,
|
|
1972
|
+
rawSource,
|
|
1973
|
+
tagRegions
|
|
1473
1974
|
};
|
|
1474
1975
|
}
|
|
1475
1976
|
|
|
@@ -2590,7 +3091,8 @@ var FormHarness = class {
|
|
|
2590
3091
|
* Applies issue filtering and computes step budget.
|
|
2591
3092
|
*/
|
|
2592
3093
|
computeStepResult(result) {
|
|
2593
|
-
const
|
|
3094
|
+
const orderFiltered = this.filterIssuesByOrder(result.issues);
|
|
3095
|
+
const limitedIssues = this.filterIssuesByScope(orderFiltered).slice(0, this.config.maxIssuesPerTurn);
|
|
2594
3096
|
const stepBudget = Math.min(this.config.maxPatchesPerTurn, limitedIssues.length);
|
|
2595
3097
|
return {
|
|
2596
3098
|
structureSummary: result.structureSummary,
|
|
@@ -2681,6 +3183,36 @@ var FormHarness = class {
|
|
|
2681
3183
|
return result;
|
|
2682
3184
|
}
|
|
2683
3185
|
/**
|
|
3186
|
+
* Filter issues by order level.
|
|
3187
|
+
*
|
|
3188
|
+
* Only includes issues for fields at the current (lowest incomplete) order level.
|
|
3189
|
+
* Fields at higher order levels are deferred until all lower-order fields are complete.
|
|
3190
|
+
* If no order attributes are used, all issues pass through (all at order 0).
|
|
3191
|
+
*/
|
|
3192
|
+
filterIssuesByOrder(issues) {
|
|
3193
|
+
const fieldOrderMap = /* @__PURE__ */ new Map();
|
|
3194
|
+
for (const group of this.form.schema.groups) {
|
|
3195
|
+
const groupOrder = group.order ?? 0;
|
|
3196
|
+
for (const field of group.children) fieldOrderMap.set(field.id, field.order ?? groupOrder);
|
|
3197
|
+
}
|
|
3198
|
+
const openOrderLevels = /* @__PURE__ */ new Set();
|
|
3199
|
+
for (const issue of issues) {
|
|
3200
|
+
const fieldId = this.getFieldIdFromRef(issue.ref, issue.scope);
|
|
3201
|
+
if (fieldId) {
|
|
3202
|
+
const order = fieldOrderMap.get(fieldId) ?? 0;
|
|
3203
|
+
openOrderLevels.add(order);
|
|
3204
|
+
} else if (issue.scope === "form") openOrderLevels.add(0);
|
|
3205
|
+
}
|
|
3206
|
+
if (openOrderLevels.size <= 1) return issues;
|
|
3207
|
+
const currentOrder = Math.min(...openOrderLevels);
|
|
3208
|
+
return issues.filter((issue) => {
|
|
3209
|
+
if (issue.scope === "form") return true;
|
|
3210
|
+
const fieldId = this.getFieldIdFromRef(issue.ref, issue.scope);
|
|
3211
|
+
if (!fieldId) return true;
|
|
3212
|
+
return (fieldOrderMap.get(fieldId) ?? 0) === currentOrder;
|
|
3213
|
+
});
|
|
3214
|
+
}
|
|
3215
|
+
/**
|
|
2684
3216
|
* Extract field ID from an issue ref.
|
|
2685
3217
|
*/
|
|
2686
3218
|
getFieldIdFromRef(ref, scope) {
|
|
@@ -2885,6 +3417,62 @@ function createMockAgent(completedForm) {
|
|
|
2885
3417
|
return new MockAgent(completedForm);
|
|
2886
3418
|
}
|
|
2887
3419
|
|
|
3420
|
+
//#endregion
|
|
3421
|
+
//#region src/engine/executionPlan.ts
|
|
3422
|
+
/**
|
|
3423
|
+
* Compute an execution plan from a parsed form.
|
|
3424
|
+
*
|
|
3425
|
+
* Walks top-level items (groups, or individual fields in implicit groups)
|
|
3426
|
+
* in document order and partitions them by their `parallel` attribute:
|
|
3427
|
+
* - Items without `parallel` go to the loose-serial pool.
|
|
3428
|
+
* - Items with the same `parallel` value form a parallel batch.
|
|
3429
|
+
*
|
|
3430
|
+
* Also computes distinct order levels across all items.
|
|
3431
|
+
*/
|
|
3432
|
+
function computeExecutionPlan(form) {
|
|
3433
|
+
const looseSerial = [];
|
|
3434
|
+
const batchMap = /* @__PURE__ */ new Map();
|
|
3435
|
+
const orderSet = /* @__PURE__ */ new Set();
|
|
3436
|
+
for (const group of form.schema.groups) if (group.implicit) for (const field of group.children) {
|
|
3437
|
+
const order = field.order ?? 0;
|
|
3438
|
+
orderSet.add(order);
|
|
3439
|
+
const item = {
|
|
3440
|
+
itemId: field.id,
|
|
3441
|
+
itemType: "field",
|
|
3442
|
+
order
|
|
3443
|
+
};
|
|
3444
|
+
if (field.parallel) {
|
|
3445
|
+
const list = batchMap.get(field.parallel) ?? [];
|
|
3446
|
+
list.push(item);
|
|
3447
|
+
batchMap.set(field.parallel, list);
|
|
3448
|
+
} else looseSerial.push(item);
|
|
3449
|
+
}
|
|
3450
|
+
else {
|
|
3451
|
+
const order = group.order ?? 0;
|
|
3452
|
+
orderSet.add(order);
|
|
3453
|
+
const item = {
|
|
3454
|
+
itemId: group.id,
|
|
3455
|
+
itemType: "group",
|
|
3456
|
+
order
|
|
3457
|
+
};
|
|
3458
|
+
if (group.parallel) {
|
|
3459
|
+
const list = batchMap.get(group.parallel) ?? [];
|
|
3460
|
+
list.push(item);
|
|
3461
|
+
batchMap.set(group.parallel, list);
|
|
3462
|
+
} else looseSerial.push(item);
|
|
3463
|
+
}
|
|
3464
|
+
const parallelBatches = [];
|
|
3465
|
+
for (const [batchId, items] of batchMap) parallelBatches.push({
|
|
3466
|
+
batchId,
|
|
3467
|
+
items
|
|
3468
|
+
});
|
|
3469
|
+
return {
|
|
3470
|
+
looseSerial,
|
|
3471
|
+
parallelBatches,
|
|
3472
|
+
orderLevels: [...orderSet].sort((a, b) => a - b)
|
|
3473
|
+
};
|
|
3474
|
+
}
|
|
3475
|
+
|
|
2888
3476
|
//#endregion
|
|
2889
3477
|
//#region ../../node_modules/.pnpm/@ai-sdk+provider@3.0.0/node_modules/@ai-sdk/provider/dist/index.mjs
|
|
2890
3478
|
var marker$1 = "vercel.ai.error";
|
|
@@ -8671,6 +9259,185 @@ function getProviderInfo(provider) {
|
|
|
8671
9259
|
};
|
|
8672
9260
|
}
|
|
8673
9261
|
|
|
9262
|
+
//#endregion
|
|
9263
|
+
//#region src/harness/parallelHarness.ts
|
|
9264
|
+
/**
|
|
9265
|
+
* Parallel Harness - Orchestrates concurrent agent execution for parallel form filling.
|
|
9266
|
+
*
|
|
9267
|
+
* Uses the execution plan to identify parallel batches and order levels,
|
|
9268
|
+
* spawns concurrent agents for batch items, merges patches, and applies them.
|
|
9269
|
+
*/
|
|
9270
|
+
/**
|
|
9271
|
+
* Get field IDs that belong to an execution plan item.
|
|
9272
|
+
*/
|
|
9273
|
+
function getFieldIdsForItem(form, item) {
|
|
9274
|
+
if (item.itemType === "field") return [item.itemId];
|
|
9275
|
+
const group = form.schema.groups.find((g) => g.id === item.itemId);
|
|
9276
|
+
if (!group) return [];
|
|
9277
|
+
return group.children.map((f) => f.id);
|
|
9278
|
+
}
|
|
9279
|
+
/**
|
|
9280
|
+
* Filter issues to only those relevant to an execution plan item's fields.
|
|
9281
|
+
* Form-scoped issues are excluded (they don't belong to individual agents).
|
|
9282
|
+
*/
|
|
9283
|
+
function scopeIssuesForItem(form, item, allIssues) {
|
|
9284
|
+
const targetFieldIds = new Set(getFieldIdsForItem(form, item));
|
|
9285
|
+
return allIssues.filter((issue) => {
|
|
9286
|
+
if (issue.scope === "form") return false;
|
|
9287
|
+
if (issue.scope === "field") return targetFieldIds.has(issue.ref);
|
|
9288
|
+
if (issue.scope === "option") {
|
|
9289
|
+
const dotIndex = issue.ref.indexOf(".");
|
|
9290
|
+
const fieldId = dotIndex > 0 ? issue.ref.slice(0, dotIndex) : issue.ref;
|
|
9291
|
+
return targetFieldIds.has(fieldId);
|
|
9292
|
+
}
|
|
9293
|
+
if (issue.scope === "cell") {
|
|
9294
|
+
const dotIndex = issue.ref.indexOf(".");
|
|
9295
|
+
const fieldId = dotIndex > 0 ? issue.ref.slice(0, dotIndex) : issue.ref;
|
|
9296
|
+
return targetFieldIds.has(fieldId);
|
|
9297
|
+
}
|
|
9298
|
+
return false;
|
|
9299
|
+
});
|
|
9300
|
+
}
|
|
9301
|
+
/**
|
|
9302
|
+
* Parallel harness that orchestrates concurrent agent execution.
|
|
9303
|
+
*/
|
|
9304
|
+
var ParallelHarness = class {
|
|
9305
|
+
form;
|
|
9306
|
+
plan;
|
|
9307
|
+
config;
|
|
9308
|
+
constructor(form, config = {}) {
|
|
9309
|
+
this.form = form;
|
|
9310
|
+
this.plan = computeExecutionPlan(form);
|
|
9311
|
+
this.config = config;
|
|
9312
|
+
}
|
|
9313
|
+
/**
|
|
9314
|
+
* Get the execution plan.
|
|
9315
|
+
*/
|
|
9316
|
+
getExecutionPlan() {
|
|
9317
|
+
return this.plan;
|
|
9318
|
+
}
|
|
9319
|
+
/**
|
|
9320
|
+
* Run a single order level: execute loose serial items with the primary agent,
|
|
9321
|
+
* and parallel batch items concurrently.
|
|
9322
|
+
*/
|
|
9323
|
+
async runOrderLevel(order, primaryAgent) {
|
|
9324
|
+
const maxPatches = this.config.maxPatchesPerTurn ?? 20;
|
|
9325
|
+
let totalPatchesApplied = 0;
|
|
9326
|
+
const errors = [];
|
|
9327
|
+
const allIssues = inspect(this.form).issues;
|
|
9328
|
+
const looseItems = this.plan.looseSerial.filter((i) => i.order === order);
|
|
9329
|
+
for (const item of looseItems) {
|
|
9330
|
+
const scopedIssues = scopeIssuesForItem(this.form, item, allIssues);
|
|
9331
|
+
if (scopedIssues.length === 0) continue;
|
|
9332
|
+
try {
|
|
9333
|
+
const response = await primaryAgent.fillFormTool(scopedIssues, this.form, maxPatches);
|
|
9334
|
+
if (response.patches.length > 0) {
|
|
9335
|
+
const result = applyPatches(this.form, response.patches);
|
|
9336
|
+
totalPatchesApplied += result.appliedPatches.length;
|
|
9337
|
+
}
|
|
9338
|
+
} catch (err) {
|
|
9339
|
+
errors.push(`Loose serial item ${item.itemId}: ${String(err)}`);
|
|
9340
|
+
}
|
|
9341
|
+
}
|
|
9342
|
+
for (const batch of this.plan.parallelBatches) {
|
|
9343
|
+
const batchItems = batch.items.filter((i) => i.order === order);
|
|
9344
|
+
if (batchItems.length === 0) continue;
|
|
9345
|
+
this.config.onBatchStart?.(batch.batchId);
|
|
9346
|
+
const batchIssues = inspect(this.form).issues;
|
|
9347
|
+
const maxConcurrent = this.config.maxParallelAgents ?? DEFAULT_MAX_PARALLEL_AGENTS;
|
|
9348
|
+
const agentPromises = [];
|
|
9349
|
+
const itemFieldIds = /* @__PURE__ */ new Map();
|
|
9350
|
+
for (let i = 0; i < batchItems.length; i++) {
|
|
9351
|
+
const item = batchItems[i];
|
|
9352
|
+
const scopedIssues = scopeIssuesForItem(this.form, item, batchIssues);
|
|
9353
|
+
const targetFieldIds = getFieldIdsForItem(this.form, item);
|
|
9354
|
+
itemFieldIds.set(i, targetFieldIds);
|
|
9355
|
+
if (scopedIssues.length === 0) {
|
|
9356
|
+
agentPromises.push(Promise.resolve({ patches: [] }));
|
|
9357
|
+
continue;
|
|
9358
|
+
}
|
|
9359
|
+
const request = {
|
|
9360
|
+
form: this.form,
|
|
9361
|
+
targetFieldIds,
|
|
9362
|
+
targetGroupIds: item.itemType === "group" ? [item.itemId] : [],
|
|
9363
|
+
issues: scopedIssues
|
|
9364
|
+
};
|
|
9365
|
+
const agent = this.config.agentFactory ? this.config.agentFactory(request) : primaryAgent;
|
|
9366
|
+
agentPromises.push(agent.fillFormTool(scopedIssues, this.form, maxPatches));
|
|
9367
|
+
}
|
|
9368
|
+
const results = await runWithConcurrency(agentPromises, maxConcurrent);
|
|
9369
|
+
const allPatches = [];
|
|
9370
|
+
for (const result of results) if (result.status === "fulfilled") allPatches.push(...result.value.patches);
|
|
9371
|
+
else errors.push(`Parallel agent error: ${result.reason}`);
|
|
9372
|
+
if (allPatches.length > 0) {
|
|
9373
|
+
const applyResult = applyPatches(this.form, allPatches);
|
|
9374
|
+
totalPatchesApplied += applyResult.appliedPatches.length;
|
|
9375
|
+
}
|
|
9376
|
+
this.config.onBatchComplete?.(batch.batchId);
|
|
9377
|
+
}
|
|
9378
|
+
return {
|
|
9379
|
+
patchesApplied: totalPatchesApplied,
|
|
9380
|
+
errors
|
|
9381
|
+
};
|
|
9382
|
+
}
|
|
9383
|
+
/**
|
|
9384
|
+
* Run all order levels sequentially, filling the entire form.
|
|
9385
|
+
*/
|
|
9386
|
+
async runAll(primaryAgent) {
|
|
9387
|
+
let totalPatchesApplied = 0;
|
|
9388
|
+
const orderLevelsProcessed = [];
|
|
9389
|
+
const allErrors = [];
|
|
9390
|
+
for (const order of this.plan.orderLevels) {
|
|
9391
|
+
this.config.onOrderLevelStart?.(order);
|
|
9392
|
+
const result = await this.runOrderLevel(order, primaryAgent);
|
|
9393
|
+
totalPatchesApplied += result.patchesApplied;
|
|
9394
|
+
orderLevelsProcessed.push(order);
|
|
9395
|
+
allErrors.push(...result.errors);
|
|
9396
|
+
this.config.onOrderLevelComplete?.(order);
|
|
9397
|
+
}
|
|
9398
|
+
return {
|
|
9399
|
+
isComplete: inspect(this.form).isComplete,
|
|
9400
|
+
totalPatchesApplied,
|
|
9401
|
+
orderLevelsProcessed,
|
|
9402
|
+
errors: allErrors
|
|
9403
|
+
};
|
|
9404
|
+
}
|
|
9405
|
+
};
|
|
9406
|
+
/**
|
|
9407
|
+
* Run promises with a concurrency limit.
|
|
9408
|
+
* Returns results in the same order as input.
|
|
9409
|
+
*/
|
|
9410
|
+
async function runWithConcurrency(promises, maxConcurrent) {
|
|
9411
|
+
if (maxConcurrent >= promises.length) return Promise.allSettled(promises);
|
|
9412
|
+
const results = new Array(promises.length);
|
|
9413
|
+
let nextIndex = 0;
|
|
9414
|
+
async function runNext() {
|
|
9415
|
+
while (nextIndex < promises.length) {
|
|
9416
|
+
const idx = nextIndex++;
|
|
9417
|
+
try {
|
|
9418
|
+
results[idx] = {
|
|
9419
|
+
status: "fulfilled",
|
|
9420
|
+
value: await promises[idx]
|
|
9421
|
+
};
|
|
9422
|
+
} catch (reason) {
|
|
9423
|
+
results[idx] = {
|
|
9424
|
+
status: "rejected",
|
|
9425
|
+
reason
|
|
9426
|
+
};
|
|
9427
|
+
}
|
|
9428
|
+
}
|
|
9429
|
+
}
|
|
9430
|
+
const workers = Array.from({ length: Math.min(maxConcurrent, promises.length) }, () => runNext());
|
|
9431
|
+
await Promise.all(workers);
|
|
9432
|
+
return results;
|
|
9433
|
+
}
|
|
9434
|
+
/**
|
|
9435
|
+
* Create a parallel harness for the given form.
|
|
9436
|
+
*/
|
|
9437
|
+
function createParallelHarness(form, config) {
|
|
9438
|
+
return new ParallelHarness(form, config);
|
|
9439
|
+
}
|
|
9440
|
+
|
|
8674
9441
|
//#endregion
|
|
8675
9442
|
//#region src/harness/programmaticFill.ts
|
|
8676
9443
|
function buildErrorResult(form, errors, warnings) {
|
|
@@ -8798,6 +9565,9 @@ async function fillForm(options) {
|
|
|
8798
9565
|
}
|
|
8799
9566
|
inputContextWarnings = coercionResult.warnings;
|
|
8800
9567
|
}
|
|
9568
|
+
if (options.enableParallel) {
|
|
9569
|
+
if (computeExecutionPlan(form).parallelBatches.length > 0) return fillFormParallel(form, model, provider, options, totalPatches, inputContextWarnings);
|
|
9570
|
+
}
|
|
8801
9571
|
const maxTurnsTotal = options.maxTurnsTotal ?? DEFAULT_MAX_TURNS;
|
|
8802
9572
|
const startingTurnNumber = options.startingTurnNumber ?? 0;
|
|
8803
9573
|
const maxPatchesPerTurn = options.maxPatchesPerTurn ?? DEFAULT_MAX_PATCHES_PER_TURN;
|
|
@@ -8903,6 +9673,169 @@ async function fillForm(options) {
|
|
|
8903
9673
|
message: `Reached maximum total turns (${maxTurnsTotal})`
|
|
8904
9674
|
}, inputContextWarnings, stepResult.issues);
|
|
8905
9675
|
}
|
|
9676
|
+
/**
|
|
9677
|
+
* Fill a form using parallel execution.
|
|
9678
|
+
*
|
|
9679
|
+
* For each order level, runs serial items with the primary agent (multi-turn),
|
|
9680
|
+
* then runs parallel batch items concurrently (multi-turn per agent).
|
|
9681
|
+
*/
|
|
9682
|
+
async function fillFormParallel(form, model, provider, options, initialPatches, inputContextWarnings) {
|
|
9683
|
+
const plan = computeExecutionPlan(form);
|
|
9684
|
+
const maxTurnsTotal = options.maxTurnsTotal ?? DEFAULT_MAX_TURNS;
|
|
9685
|
+
const startingTurnNumber = options.startingTurnNumber ?? 0;
|
|
9686
|
+
const maxPatchesPerTurn = options.maxPatchesPerTurn ?? DEFAULT_MAX_PATCHES_PER_TURN;
|
|
9687
|
+
const maxIssuesPerTurn = options.maxIssuesPerTurn ?? DEFAULT_MAX_ISSUES_PER_TURN;
|
|
9688
|
+
const maxParallelAgents = options.maxParallelAgents ?? DEFAULT_MAX_PARALLEL_AGENTS;
|
|
9689
|
+
const targetRoles = options.targetRoles ?? [AGENT_ROLE];
|
|
9690
|
+
let totalPatches = initialPatches;
|
|
9691
|
+
let turnCount = startingTurnNumber;
|
|
9692
|
+
const primaryAgent = options._testAgent ?? createLiveAgent({
|
|
9693
|
+
model,
|
|
9694
|
+
systemPromptAddition: options.systemPromptAddition,
|
|
9695
|
+
targetRole: targetRoles[0] ?? AGENT_ROLE,
|
|
9696
|
+
provider,
|
|
9697
|
+
enableWebSearch: options.enableWebSearch,
|
|
9698
|
+
additionalTools: options.additionalTools,
|
|
9699
|
+
callbacks: options.callbacks,
|
|
9700
|
+
maxStepsPerTurn: options.maxStepsPerTurn
|
|
9701
|
+
});
|
|
9702
|
+
for (const order of plan.orderLevels) {
|
|
9703
|
+
if (options.signal?.aborted) return buildResult(form, turnCount, totalPatches, {
|
|
9704
|
+
ok: false,
|
|
9705
|
+
reason: "cancelled"
|
|
9706
|
+
}, inputContextWarnings);
|
|
9707
|
+
if (turnCount >= maxTurnsTotal) return buildResult(form, turnCount, totalPatches, {
|
|
9708
|
+
ok: false,
|
|
9709
|
+
reason: "max_turns",
|
|
9710
|
+
message: `Reached maximum total turns (${maxTurnsTotal})`
|
|
9711
|
+
}, inputContextWarnings);
|
|
9712
|
+
try {
|
|
9713
|
+
options.callbacks?.onOrderLevelStart?.({ order });
|
|
9714
|
+
} catch {}
|
|
9715
|
+
const serialItems = plan.looseSerial.filter((i) => i.order === order);
|
|
9716
|
+
if (serialItems.length > 0) {
|
|
9717
|
+
const result = await runMultiTurnForItems(form, primaryAgent, serialItems, targetRoles, maxPatchesPerTurn, maxIssuesPerTurn, maxTurnsTotal, turnCount, options);
|
|
9718
|
+
totalPatches += result.patchesApplied;
|
|
9719
|
+
turnCount += result.turnsUsed;
|
|
9720
|
+
if (result.aborted) return buildResult(form, turnCount, totalPatches, result.status, inputContextWarnings);
|
|
9721
|
+
}
|
|
9722
|
+
for (const batch of plan.parallelBatches) {
|
|
9723
|
+
const batchItems = batch.items.filter((i) => i.order === order);
|
|
9724
|
+
if (batchItems.length === 0) continue;
|
|
9725
|
+
try {
|
|
9726
|
+
options.callbacks?.onBatchStart?.({
|
|
9727
|
+
batchId: batch.batchId,
|
|
9728
|
+
itemCount: batchItems.length
|
|
9729
|
+
});
|
|
9730
|
+
} catch {}
|
|
9731
|
+
const results = await runWithConcurrency(batchItems.map((item) => {
|
|
9732
|
+
return runMultiTurnForItems(form, options._testAgent ?? createLiveAgent({
|
|
9733
|
+
model,
|
|
9734
|
+
systemPromptAddition: options.systemPromptAddition,
|
|
9735
|
+
targetRole: targetRoles[0] ?? AGENT_ROLE,
|
|
9736
|
+
provider,
|
|
9737
|
+
enableWebSearch: options.enableWebSearch,
|
|
9738
|
+
additionalTools: options.additionalTools,
|
|
9739
|
+
callbacks: options.callbacks,
|
|
9740
|
+
maxStepsPerTurn: options.maxStepsPerTurn
|
|
9741
|
+
}), [item], targetRoles, maxPatchesPerTurn, maxIssuesPerTurn, maxTurnsTotal, turnCount, options);
|
|
9742
|
+
}).map((p) => p), maxParallelAgents);
|
|
9743
|
+
let batchPatches = 0;
|
|
9744
|
+
for (const result of results) if (result.status === "fulfilled") {
|
|
9745
|
+
totalPatches += result.value.patchesApplied;
|
|
9746
|
+
batchPatches += result.value.patchesApplied;
|
|
9747
|
+
turnCount += result.value.turnsUsed;
|
|
9748
|
+
}
|
|
9749
|
+
try {
|
|
9750
|
+
options.callbacks?.onBatchComplete?.({
|
|
9751
|
+
batchId: batch.batchId,
|
|
9752
|
+
patchesApplied: batchPatches
|
|
9753
|
+
});
|
|
9754
|
+
} catch {}
|
|
9755
|
+
}
|
|
9756
|
+
const levelInspect = inspect(form);
|
|
9757
|
+
try {
|
|
9758
|
+
options.callbacks?.onOrderLevelComplete?.({
|
|
9759
|
+
order,
|
|
9760
|
+
patchesApplied: totalPatches
|
|
9761
|
+
});
|
|
9762
|
+
} catch {}
|
|
9763
|
+
if (levelInspect.isComplete) return buildResult(form, turnCount, totalPatches, { ok: true }, inputContextWarnings);
|
|
9764
|
+
}
|
|
9765
|
+
const finalInspect = inspect(form);
|
|
9766
|
+
if (finalInspect.isComplete) return buildResult(form, turnCount, totalPatches, { ok: true }, inputContextWarnings);
|
|
9767
|
+
return buildResult(form, turnCount, totalPatches, {
|
|
9768
|
+
ok: false,
|
|
9769
|
+
reason: "max_turns",
|
|
9770
|
+
message: `Reached maximum total turns (${maxTurnsTotal})`
|
|
9771
|
+
}, inputContextWarnings, finalInspect.issues);
|
|
9772
|
+
}
|
|
9773
|
+
/**
|
|
9774
|
+
* Run a multi-turn loop for a set of execution plan items.
|
|
9775
|
+
* Scoped issues are filtered to only the target items' fields.
|
|
9776
|
+
* Retries with rejection feedback, same as the serial fillForm path.
|
|
9777
|
+
*/
|
|
9778
|
+
async function runMultiTurnForItems(form, agent, items, targetRoles, maxPatchesPerTurn, _maxIssuesPerTurn, maxTurnsTotal, startTurn, options) {
|
|
9779
|
+
let turnsUsed = 0;
|
|
9780
|
+
let patchesApplied = 0;
|
|
9781
|
+
let previousRejections;
|
|
9782
|
+
const maxTurnsForItems = Math.min(maxTurnsTotal - startTurn, options.maxTurnsThisCall ?? Infinity);
|
|
9783
|
+
for (let turn = 0; turn < maxTurnsForItems; turn++) {
|
|
9784
|
+
if (options.signal?.aborted) return {
|
|
9785
|
+
patchesApplied,
|
|
9786
|
+
turnsUsed,
|
|
9787
|
+
aborted: true,
|
|
9788
|
+
status: {
|
|
9789
|
+
ok: false,
|
|
9790
|
+
reason: "cancelled"
|
|
9791
|
+
}
|
|
9792
|
+
};
|
|
9793
|
+
const allIssues = inspect(form, { targetRoles }).issues;
|
|
9794
|
+
let scopedIssues = [];
|
|
9795
|
+
for (const item of items) scopedIssues.push(...scopeIssuesForItem(form, item, allIssues));
|
|
9796
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9797
|
+
scopedIssues = scopedIssues.filter((issue) => {
|
|
9798
|
+
const key = `${issue.scope}:${issue.ref}:${issue.message}`;
|
|
9799
|
+
if (seen.has(key)) return false;
|
|
9800
|
+
seen.add(key);
|
|
9801
|
+
return true;
|
|
9802
|
+
});
|
|
9803
|
+
if (scopedIssues.length === 0) break;
|
|
9804
|
+
try {
|
|
9805
|
+
options.callbacks?.onTurnStart?.({
|
|
9806
|
+
turnNumber: startTurn + turnsUsed + 1,
|
|
9807
|
+
issuesCount: scopedIssues.length
|
|
9808
|
+
});
|
|
9809
|
+
} catch {}
|
|
9810
|
+
const response = await agent.fillFormTool(scopedIssues, form, maxPatchesPerTurn, previousRejections);
|
|
9811
|
+
if (response.patches.length > 0) {
|
|
9812
|
+
const applyResult = applyPatches(form, response.patches);
|
|
9813
|
+
patchesApplied += applyResult.appliedPatches.length;
|
|
9814
|
+
previousRejections = applyResult.rejectedPatches;
|
|
9815
|
+
} else previousRejections = void 0;
|
|
9816
|
+
turnsUsed++;
|
|
9817
|
+
try {
|
|
9818
|
+
const postInspect = inspect(form, { targetRoles });
|
|
9819
|
+
const requiredIssues = postInspect.issues.filter((i) => i.severity === "required");
|
|
9820
|
+
options.callbacks?.onTurnComplete?.({
|
|
9821
|
+
turnNumber: startTurn + turnsUsed,
|
|
9822
|
+
issuesShown: scopedIssues.length,
|
|
9823
|
+
patchesApplied: response.patches.length,
|
|
9824
|
+
requiredIssuesRemaining: requiredIssues.length,
|
|
9825
|
+
isComplete: postInspect.isComplete,
|
|
9826
|
+
stats: response.stats,
|
|
9827
|
+
issues: scopedIssues,
|
|
9828
|
+
patches: response.patches,
|
|
9829
|
+
rejectedPatches: previousRejections ?? []
|
|
9830
|
+
});
|
|
9831
|
+
} catch {}
|
|
9832
|
+
}
|
|
9833
|
+
return {
|
|
9834
|
+
patchesApplied,
|
|
9835
|
+
turnsUsed,
|
|
9836
|
+
aborted: false
|
|
9837
|
+
};
|
|
9838
|
+
}
|
|
8906
9839
|
|
|
8907
9840
|
//#endregion
|
|
8908
9841
|
//#region src/harness/harnessConfigResolver.ts
|
|
@@ -8924,6 +9857,7 @@ function resolveHarnessConfig(form, options) {
|
|
|
8924
9857
|
maxTurns: options?.maxTurnsTotal ?? frontmatterConfig?.maxTurns ?? DEFAULT_MAX_TURNS,
|
|
8925
9858
|
maxPatchesPerTurn: options?.maxPatchesPerTurn ?? frontmatterConfig?.maxPatchesPerTurn ?? DEFAULT_MAX_PATCHES_PER_TURN,
|
|
8926
9859
|
maxIssuesPerTurn: options?.maxIssuesPerTurn ?? frontmatterConfig?.maxIssuesPerTurn ?? DEFAULT_MAX_ISSUES_PER_TURN,
|
|
9860
|
+
maxParallelAgents: options?.maxParallelAgents ?? frontmatterConfig?.maxParallelAgents ?? DEFAULT_MAX_PARALLEL_AGENTS,
|
|
8927
9861
|
targetRoles: options?.targetRoles,
|
|
8928
9862
|
fillMode: options?.fillMode
|
|
8929
9863
|
};
|
|
@@ -9048,8 +9982,8 @@ function validateResearchForm(form) {
|
|
|
9048
9982
|
//#endregion
|
|
9049
9983
|
//#region src/index.ts
|
|
9050
9984
|
/** Markform version (injected at build time). */
|
|
9051
|
-
const VERSION = "0.1.
|
|
9985
|
+
const VERSION = "0.1.18";
|
|
9052
9986
|
|
|
9053
9987
|
//#endregion
|
|
9054
|
-
export {
|
|
9055
|
-
//# sourceMappingURL=src-
|
|
9988
|
+
export { formToJsonSchema as A, getFieldId as C, parseScopeRef as D, isQualifiedRef as E, findAllHeadings as F, findEnclosingHeadings as I, parseCellValue as L, findAllCheckboxes as M, injectCheckboxIds as N, serializeScopeRef as O, injectHeaderIds as P, parseMarkdownTable as R, findFieldById as S, isFieldRef as T, createMockAgent as _, resolveHarnessConfig as a, coerceInputContext as b, createParallelHarness as c, getProviderNames as d, resolveModel as f, MockAgent as g, computeExecutionPlan as h, runResearch as i, parseForm as j, fieldToJsonSchema as k, scopeIssuesForItem as l, createLiveAgent as m, isResearchForm as n, fillForm as o, buildMockWireFormat as p, validateResearchForm as r, ParallelHarness as s, VERSION as t, getProviderInfo as u, FormHarness as v, isCellRef as w, coerceToFieldPatch as x, createHarness as y, parseRawTable as z };
|
|
9989
|
+
//# sourceMappingURL=src-DDxi-2ne.mjs.map
|