markform 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -16
- package/dist/ai-sdk.d.mts +1 -1
- package/dist/ai-sdk.mjs +48 -6
- package/dist/ai-sdk.mjs.map +1 -1
- package/dist/bin.mjs +1 -1
- package/dist/{cli-C8F9yDsv.mjs → cli-B1DhFYBS.mjs} +642 -96
- package/dist/cli-B1DhFYBS.mjs.map +1 -0
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/{coreTypes-CTLr-NGd.mjs → coreTypes-CctFK6uE.mjs} +38 -2
- package/dist/coreTypes-CctFK6uE.mjs.map +1 -0
- package/dist/{coreTypes-BlsJkU1w.d.mts → coreTypes-GxzWNXap.d.mts} +137 -3
- package/dist/{fillRecord-DTl5lnK0.d.mts → fillRecord-DeqI2pQ5.d.mts} +25 -1
- package/dist/{fillRecordRenderer-CruJrLkj.mjs → fillRecordRenderer-VBQ2vwPV.mjs} +2 -5
- package/dist/fillRecordRenderer-VBQ2vwPV.mjs.map +1 -0
- package/dist/index.d.mts +52 -29
- package/dist/index.mjs +5 -5
- package/dist/{apply-C7mO7VkZ.mjs → prompts-BCnYaH4_.mjs} +969 -8
- package/dist/prompts-BCnYaH4_.mjs.map +1 -0
- package/dist/render.d.mts +2 -2
- package/dist/render.mjs +1 -1
- package/dist/{session-BCcltrLA.mjs → session-BLjN3BkJ.mjs} +2 -2
- package/dist/{session-BCcltrLA.mjs.map → session-BLjN3BkJ.mjs.map} +1 -1
- package/dist/{session-VeSkVrck.mjs → session-D7C7IlEv.mjs} +1 -1
- package/dist/{shared-CsdT2T7k.mjs → shared-CuSRYcIB.mjs} +3 -3
- package/dist/shared-CuSRYcIB.mjs.map +1 -0
- package/dist/{shared-fb0nkzQi.mjs → shared-DtorFV21.mjs} +1 -1
- package/dist/{src-CbRnGzMK.mjs → src-C5OWf1dL.mjs} +114 -826
- package/dist/src-C5OWf1dL.mjs.map +1 -0
- package/docs/markform-apis.md +6 -0
- package/docs/markform-reference.md +26 -1
- package/docs/markform-spec.md +11 -3
- package/docs/skill/SKILL.md +119 -0
- package/examples/rejection-test/rejection-test.session.yaml +52 -0
- package/examples/simple/simple-with-skips.session.yaml +78 -0
- package/examples/simple/simple.session.yaml +78 -0
- package/package.json +2 -2
- package/dist/apply-C7mO7VkZ.mjs.map +0 -1
- package/dist/cli-C8F9yDsv.mjs.map +0 -1
- package/dist/coreTypes-CTLr-NGd.mjs.map +0 -1
- package/dist/fillRecordRenderer-CruJrLkj.mjs.map +0 -1
- package/dist/shared-CsdT2T7k.mjs.map +0 -1
- package/dist/src-CbRnGzMK.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { A as MarkformSectionInputSchema, R as PatchSchema,
|
|
3
|
-
import {
|
|
2
|
+
import { A as MarkformSectionInputSchema, B as ProgressCountsSchema, R as PatchSchema, ht as StructureSummarySchema, z as PatchWarningSchema } from "./coreTypes-CctFK6uE.mjs";
|
|
3
|
+
import { $ as DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN, A as tryParseSentinelResponse, B as isTagNode, D as detectSyntaxStyle, F as getBooleanAttr, G as DEFAULT_MAX_PARALLEL_AGENTS, H as AGENT_ROLE, I as getNumberAttr, J as DEFAULT_MAX_STEPS_PER_TURN, K as DEFAULT_MAX_PATCHES_PER_TURN, L as getStringArrayAttr, M as extractFenceValue, N as extractOptionItems, O as preprocessCommentSyntax, P as extractTableContent, Pt as wrapApiError, Q as DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN, R as getStringAttr, S as computeStructureSummary, St as MarkformParseError, V as parseOptionText, W as DEFAULT_MAX_ISSUES_PER_TURN, Y as DEFAULT_MAX_TURNS, Z as DEFAULT_PRIORITY, _ as inspect, a as WEB_SEARCH_INSTRUCTIONS, c as filterIssuesByOrder, d as coerceInputContext, dt as transformHarnessConfigToTs, et as DEFAULT_ROLES, g as getFieldsForRoles, ht as getWebSearchConfig, i as SECTION_HEADERS, j as CHECKBOX_MARKERS, l as filterIssuesByScope, m as applyPatches, n as GENERAL_INSTRUCTIONS, o as getIssuesIntro, q as DEFAULT_MAX_RETRIES, r as ISSUES_HEADER, s as getPatchFormatHint, t as DEFAULT_SYSTEM_PROMPT, tt as DEFAULT_ROLE_INSTRUCTIONS, w as serializeForm, x as computeProgressSummary, yt as MarkformConfigError, z as getValidateAttr } from "./prompts-BCnYaH4_.mjs";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import Markdoc from "@markdoc/markdoc";
|
|
6
6
|
import YAML from "yaml";
|
|
@@ -2154,549 +2154,6 @@ function getFieldId(ref) {
|
|
|
2154
2154
|
return ref.fieldId;
|
|
2155
2155
|
}
|
|
2156
2156
|
|
|
2157
|
-
//#endregion
|
|
2158
|
-
//#region src/engine/valueCoercion.ts
|
|
2159
|
-
/**
|
|
2160
|
-
* Find a field by ID.
|
|
2161
|
-
*
|
|
2162
|
-
* Uses idIndex for O(1) validation that the ID exists and is a field,
|
|
2163
|
-
* then retrieves the Field object from the schema.
|
|
2164
|
-
*/
|
|
2165
|
-
function findFieldById(form, fieldId) {
|
|
2166
|
-
if (form.idIndex.get(fieldId)?.nodeType !== "field") return;
|
|
2167
|
-
for (const group of form.schema.groups) for (const field of group.children) if (field.id === fieldId) return field;
|
|
2168
|
-
}
|
|
2169
|
-
function isPlainObject(value) {
|
|
2170
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2171
|
-
}
|
|
2172
|
-
function isStringArray(value) {
|
|
2173
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
2174
|
-
}
|
|
2175
|
-
function coerceToString(fieldId, rawValue) {
|
|
2176
|
-
if (rawValue === null) return {
|
|
2177
|
-
ok: true,
|
|
2178
|
-
patch: {
|
|
2179
|
-
op: "set_string",
|
|
2180
|
-
fieldId,
|
|
2181
|
-
value: null
|
|
2182
|
-
}
|
|
2183
|
-
};
|
|
2184
|
-
if (typeof rawValue === "string") return {
|
|
2185
|
-
ok: true,
|
|
2186
|
-
patch: {
|
|
2187
|
-
op: "set_string",
|
|
2188
|
-
fieldId,
|
|
2189
|
-
value: rawValue
|
|
2190
|
-
}
|
|
2191
|
-
};
|
|
2192
|
-
if (typeof rawValue === "number") return {
|
|
2193
|
-
ok: true,
|
|
2194
|
-
patch: {
|
|
2195
|
-
op: "set_string",
|
|
2196
|
-
fieldId,
|
|
2197
|
-
value: String(rawValue)
|
|
2198
|
-
},
|
|
2199
|
-
warning: `Coerced number ${rawValue} to string for field '${fieldId}'`
|
|
2200
|
-
};
|
|
2201
|
-
if (typeof rawValue === "boolean") return {
|
|
2202
|
-
ok: true,
|
|
2203
|
-
patch: {
|
|
2204
|
-
op: "set_string",
|
|
2205
|
-
fieldId,
|
|
2206
|
-
value: String(rawValue)
|
|
2207
|
-
},
|
|
2208
|
-
warning: `Coerced boolean ${rawValue} to string for field '${fieldId}'`
|
|
2209
|
-
};
|
|
2210
|
-
return {
|
|
2211
|
-
ok: false,
|
|
2212
|
-
error: `Cannot coerce ${typeof rawValue} to string for field '${fieldId}'`
|
|
2213
|
-
};
|
|
2214
|
-
}
|
|
2215
|
-
function coerceToNumber(fieldId, rawValue) {
|
|
2216
|
-
if (rawValue === null) return {
|
|
2217
|
-
ok: true,
|
|
2218
|
-
patch: {
|
|
2219
|
-
op: "set_number",
|
|
2220
|
-
fieldId,
|
|
2221
|
-
value: null
|
|
2222
|
-
}
|
|
2223
|
-
};
|
|
2224
|
-
if (typeof rawValue === "number") return {
|
|
2225
|
-
ok: true,
|
|
2226
|
-
patch: {
|
|
2227
|
-
op: "set_number",
|
|
2228
|
-
fieldId,
|
|
2229
|
-
value: rawValue
|
|
2230
|
-
}
|
|
2231
|
-
};
|
|
2232
|
-
if (typeof rawValue === "string") {
|
|
2233
|
-
const parsed = Number(rawValue);
|
|
2234
|
-
if (!Number.isNaN(parsed)) return {
|
|
2235
|
-
ok: true,
|
|
2236
|
-
patch: {
|
|
2237
|
-
op: "set_number",
|
|
2238
|
-
fieldId,
|
|
2239
|
-
value: parsed
|
|
2240
|
-
},
|
|
2241
|
-
warning: `Coerced string '${rawValue}' to number for field '${fieldId}'`
|
|
2242
|
-
};
|
|
2243
|
-
return {
|
|
2244
|
-
ok: false,
|
|
2245
|
-
error: `Cannot coerce non-numeric string '${rawValue}' to number for field '${fieldId}'`
|
|
2246
|
-
};
|
|
2247
|
-
}
|
|
2248
|
-
return {
|
|
2249
|
-
ok: false,
|
|
2250
|
-
error: `Cannot coerce ${typeof rawValue} to number for field '${fieldId}'`
|
|
2251
|
-
};
|
|
2252
|
-
}
|
|
2253
|
-
function coerceToStringList(fieldId, rawValue) {
|
|
2254
|
-
if (rawValue === null) return {
|
|
2255
|
-
ok: true,
|
|
2256
|
-
patch: {
|
|
2257
|
-
op: "set_string_list",
|
|
2258
|
-
fieldId,
|
|
2259
|
-
value: []
|
|
2260
|
-
}
|
|
2261
|
-
};
|
|
2262
|
-
if (isStringArray(rawValue)) return {
|
|
2263
|
-
ok: true,
|
|
2264
|
-
patch: {
|
|
2265
|
-
op: "set_string_list",
|
|
2266
|
-
fieldId,
|
|
2267
|
-
value: rawValue
|
|
2268
|
-
}
|
|
2269
|
-
};
|
|
2270
|
-
if (typeof rawValue === "string") return {
|
|
2271
|
-
ok: true,
|
|
2272
|
-
patch: {
|
|
2273
|
-
op: "set_string_list",
|
|
2274
|
-
fieldId,
|
|
2275
|
-
value: [rawValue]
|
|
2276
|
-
},
|
|
2277
|
-
warning: `Coerced single string to array for field '${fieldId}'`
|
|
2278
|
-
};
|
|
2279
|
-
if (Array.isArray(rawValue)) {
|
|
2280
|
-
const items = [];
|
|
2281
|
-
for (const item of rawValue) if (typeof item === "string") items.push(item);
|
|
2282
|
-
else if (typeof item === "number" || typeof item === "boolean") items.push(String(item));
|
|
2283
|
-
else return {
|
|
2284
|
-
ok: false,
|
|
2285
|
-
error: `Cannot coerce array with non-string items to string_list for field '${fieldId}'`
|
|
2286
|
-
};
|
|
2287
|
-
return {
|
|
2288
|
-
ok: true,
|
|
2289
|
-
patch: {
|
|
2290
|
-
op: "set_string_list",
|
|
2291
|
-
fieldId,
|
|
2292
|
-
value: items
|
|
2293
|
-
},
|
|
2294
|
-
warning: `Coerced array items to strings for field '${fieldId}'`
|
|
2295
|
-
};
|
|
2296
|
-
}
|
|
2297
|
-
return {
|
|
2298
|
-
ok: false,
|
|
2299
|
-
error: `Cannot coerce ${typeof rawValue} to string_list for field '${fieldId}'`
|
|
2300
|
-
};
|
|
2301
|
-
}
|
|
2302
|
-
function coerceToSingleSelect(field, rawValue) {
|
|
2303
|
-
if (field.kind !== "single_select") return {
|
|
2304
|
-
ok: false,
|
|
2305
|
-
error: `Field '${field.id}' is not a single_select field`
|
|
2306
|
-
};
|
|
2307
|
-
if (rawValue === null) return {
|
|
2308
|
-
ok: true,
|
|
2309
|
-
patch: {
|
|
2310
|
-
op: "set_single_select",
|
|
2311
|
-
fieldId: field.id,
|
|
2312
|
-
value: null
|
|
2313
|
-
}
|
|
2314
|
-
};
|
|
2315
|
-
if (typeof rawValue !== "string") return {
|
|
2316
|
-
ok: false,
|
|
2317
|
-
error: `single_select field '${field.id}' requires a string option ID, got ${typeof rawValue}`
|
|
2318
|
-
};
|
|
2319
|
-
const validOptions = new Set(field.options.map((o) => o.id));
|
|
2320
|
-
if (!validOptions.has(rawValue)) return {
|
|
2321
|
-
ok: false,
|
|
2322
|
-
error: `Invalid option '${rawValue}' for single_select field '${field.id}'. Valid options: ${Array.from(validOptions).join(", ")}`
|
|
2323
|
-
};
|
|
2324
|
-
return {
|
|
2325
|
-
ok: true,
|
|
2326
|
-
patch: {
|
|
2327
|
-
op: "set_single_select",
|
|
2328
|
-
fieldId: field.id,
|
|
2329
|
-
value: rawValue
|
|
2330
|
-
}
|
|
2331
|
-
};
|
|
2332
|
-
}
|
|
2333
|
-
function coerceToMultiSelect(field, rawValue) {
|
|
2334
|
-
if (field.kind !== "multi_select") return {
|
|
2335
|
-
ok: false,
|
|
2336
|
-
error: `Field '${field.id}' is not a multi_select field`
|
|
2337
|
-
};
|
|
2338
|
-
if (rawValue === null) return {
|
|
2339
|
-
ok: true,
|
|
2340
|
-
patch: {
|
|
2341
|
-
op: "set_multi_select",
|
|
2342
|
-
fieldId: field.id,
|
|
2343
|
-
value: []
|
|
2344
|
-
}
|
|
2345
|
-
};
|
|
2346
|
-
const validOptions = new Set(field.options.map((o) => o.id));
|
|
2347
|
-
let selected;
|
|
2348
|
-
let warning;
|
|
2349
|
-
if (typeof rawValue === "string") {
|
|
2350
|
-
selected = [rawValue];
|
|
2351
|
-
warning = `Coerced single string to array for multi_select field '${field.id}'`;
|
|
2352
|
-
} else if (isStringArray(rawValue)) selected = rawValue;
|
|
2353
|
-
else return {
|
|
2354
|
-
ok: false,
|
|
2355
|
-
error: `multi_select field '${field.id}' requires a string or string array, got ${typeof rawValue}`
|
|
2356
|
-
};
|
|
2357
|
-
for (const optId of selected) if (!validOptions.has(optId)) return {
|
|
2358
|
-
ok: false,
|
|
2359
|
-
error: `Invalid option '${optId}' for multi_select field '${field.id}'. Valid options: ${Array.from(validOptions).join(", ")}`
|
|
2360
|
-
};
|
|
2361
|
-
const patch = {
|
|
2362
|
-
op: "set_multi_select",
|
|
2363
|
-
fieldId: field.id,
|
|
2364
|
-
value: selected
|
|
2365
|
-
};
|
|
2366
|
-
return warning ? {
|
|
2367
|
-
ok: true,
|
|
2368
|
-
patch,
|
|
2369
|
-
warning
|
|
2370
|
-
} : {
|
|
2371
|
-
ok: true,
|
|
2372
|
-
patch
|
|
2373
|
-
};
|
|
2374
|
-
}
|
|
2375
|
-
function coerceToCheckboxes(field, rawValue) {
|
|
2376
|
-
if (field.kind !== "checkboxes") return {
|
|
2377
|
-
ok: false,
|
|
2378
|
-
error: `Field '${field.id}' is not a checkboxes field`
|
|
2379
|
-
};
|
|
2380
|
-
if (rawValue === null) return {
|
|
2381
|
-
ok: true,
|
|
2382
|
-
patch: {
|
|
2383
|
-
op: "set_checkboxes",
|
|
2384
|
-
fieldId: field.id,
|
|
2385
|
-
value: {}
|
|
2386
|
-
}
|
|
2387
|
-
};
|
|
2388
|
-
const validOptions = new Set(field.options.map((o) => o.id));
|
|
2389
|
-
const checkboxMode = field.checkboxMode;
|
|
2390
|
-
if (Array.isArray(rawValue)) {
|
|
2391
|
-
const defaultState = checkboxMode === "explicit" ? "yes" : "done";
|
|
2392
|
-
const values = {};
|
|
2393
|
-
for (const item of rawValue) {
|
|
2394
|
-
if (typeof item !== "string") return {
|
|
2395
|
-
ok: false,
|
|
2396
|
-
error: `Array items for checkboxes field '${field.id}' must be strings (option IDs), got ${typeof item}`
|
|
2397
|
-
};
|
|
2398
|
-
if (!validOptions.has(item)) return {
|
|
2399
|
-
ok: false,
|
|
2400
|
-
error: `Invalid option '${item}' for checkboxes field '${field.id}'. Valid options: ${Array.from(validOptions).join(", ")}`
|
|
2401
|
-
};
|
|
2402
|
-
values[item] = defaultState;
|
|
2403
|
-
}
|
|
2404
|
-
const patch = {
|
|
2405
|
-
op: "set_checkboxes",
|
|
2406
|
-
fieldId: field.id,
|
|
2407
|
-
value: values
|
|
2408
|
-
};
|
|
2409
|
-
if (rawValue.length === 0) return {
|
|
2410
|
-
ok: true,
|
|
2411
|
-
patch
|
|
2412
|
-
};
|
|
2413
|
-
return {
|
|
2414
|
-
ok: true,
|
|
2415
|
-
patch,
|
|
2416
|
-
warning: `Coerced array to checkboxes object with '${defaultState}' state for field '${field.id}'`
|
|
2417
|
-
};
|
|
2418
|
-
}
|
|
2419
|
-
if (!isPlainObject(rawValue)) return {
|
|
2420
|
-
ok: false,
|
|
2421
|
-
error: `checkboxes field '${field.id}' requires a Record<string, CheckboxValue> or array of option IDs, got ${typeof rawValue}`
|
|
2422
|
-
};
|
|
2423
|
-
const values = {};
|
|
2424
|
-
let hadBooleanCoercion = false;
|
|
2425
|
-
const validValues = new Set(checkboxMode === "explicit" ? [
|
|
2426
|
-
"unfilled",
|
|
2427
|
-
"yes",
|
|
2428
|
-
"no"
|
|
2429
|
-
] : checkboxMode === "simple" ? ["todo", "done"] : [
|
|
2430
|
-
"todo",
|
|
2431
|
-
"done",
|
|
2432
|
-
"incomplete",
|
|
2433
|
-
"active",
|
|
2434
|
-
"na"
|
|
2435
|
-
]);
|
|
2436
|
-
for (const [optId, value] of Object.entries(rawValue)) {
|
|
2437
|
-
if (!validOptions.has(optId)) return {
|
|
2438
|
-
ok: false,
|
|
2439
|
-
error: `Invalid option '${optId}' for checkboxes field '${field.id}'. Valid options: ${Array.from(validOptions).join(", ")}`
|
|
2440
|
-
};
|
|
2441
|
-
if (typeof value === "boolean") {
|
|
2442
|
-
hadBooleanCoercion = true;
|
|
2443
|
-
if (checkboxMode === "explicit") values[optId] = value ? "yes" : "no";
|
|
2444
|
-
else values[optId] = value ? "done" : "todo";
|
|
2445
|
-
continue;
|
|
2446
|
-
}
|
|
2447
|
-
if (typeof value !== "string" || !validValues.has(value)) return {
|
|
2448
|
-
ok: false,
|
|
2449
|
-
error: `Invalid checkbox value '${String(value)}' for option '${optId}' in field '${field.id}'. Valid values for ${checkboxMode} mode: ${Array.from(validValues).join(", ")} (or use true/false)`
|
|
2450
|
-
};
|
|
2451
|
-
values[optId] = value;
|
|
2452
|
-
}
|
|
2453
|
-
const patch = {
|
|
2454
|
-
op: "set_checkboxes",
|
|
2455
|
-
fieldId: field.id,
|
|
2456
|
-
value: values
|
|
2457
|
-
};
|
|
2458
|
-
if (hadBooleanCoercion) return {
|
|
2459
|
-
ok: true,
|
|
2460
|
-
patch,
|
|
2461
|
-
warning: `Coerced boolean values to checkbox strings for field '${field.id}'`
|
|
2462
|
-
};
|
|
2463
|
-
return {
|
|
2464
|
-
ok: true,
|
|
2465
|
-
patch
|
|
2466
|
-
};
|
|
2467
|
-
}
|
|
2468
|
-
function coerceToUrl(fieldId, rawValue) {
|
|
2469
|
-
if (rawValue === null) return {
|
|
2470
|
-
ok: true,
|
|
2471
|
-
patch: {
|
|
2472
|
-
op: "set_url",
|
|
2473
|
-
fieldId,
|
|
2474
|
-
value: null
|
|
2475
|
-
}
|
|
2476
|
-
};
|
|
2477
|
-
if (typeof rawValue === "string") return {
|
|
2478
|
-
ok: true,
|
|
2479
|
-
patch: {
|
|
2480
|
-
op: "set_url",
|
|
2481
|
-
fieldId,
|
|
2482
|
-
value: rawValue
|
|
2483
|
-
}
|
|
2484
|
-
};
|
|
2485
|
-
return {
|
|
2486
|
-
ok: false,
|
|
2487
|
-
error: `Cannot coerce ${typeof rawValue} to url for field '${fieldId}'`
|
|
2488
|
-
};
|
|
2489
|
-
}
|
|
2490
|
-
function coerceToUrlList(fieldId, rawValue) {
|
|
2491
|
-
if (rawValue === null) return {
|
|
2492
|
-
ok: true,
|
|
2493
|
-
patch: {
|
|
2494
|
-
op: "set_url_list",
|
|
2495
|
-
fieldId,
|
|
2496
|
-
value: []
|
|
2497
|
-
}
|
|
2498
|
-
};
|
|
2499
|
-
if (isStringArray(rawValue)) return {
|
|
2500
|
-
ok: true,
|
|
2501
|
-
patch: {
|
|
2502
|
-
op: "set_url_list",
|
|
2503
|
-
fieldId,
|
|
2504
|
-
value: rawValue
|
|
2505
|
-
}
|
|
2506
|
-
};
|
|
2507
|
-
if (typeof rawValue === "string") return {
|
|
2508
|
-
ok: true,
|
|
2509
|
-
patch: {
|
|
2510
|
-
op: "set_url_list",
|
|
2511
|
-
fieldId,
|
|
2512
|
-
value: [rawValue]
|
|
2513
|
-
},
|
|
2514
|
-
warning: `Coerced single string to array for field '${fieldId}'`
|
|
2515
|
-
};
|
|
2516
|
-
if (Array.isArray(rawValue)) {
|
|
2517
|
-
const items = [];
|
|
2518
|
-
for (const item of rawValue) if (typeof item === "string") items.push(item);
|
|
2519
|
-
else return {
|
|
2520
|
-
ok: false,
|
|
2521
|
-
error: `Cannot coerce array with non-string items to url_list for field '${fieldId}'`
|
|
2522
|
-
};
|
|
2523
|
-
return {
|
|
2524
|
-
ok: true,
|
|
2525
|
-
patch: {
|
|
2526
|
-
op: "set_url_list",
|
|
2527
|
-
fieldId,
|
|
2528
|
-
value: items
|
|
2529
|
-
}
|
|
2530
|
-
};
|
|
2531
|
-
}
|
|
2532
|
-
return {
|
|
2533
|
-
ok: false,
|
|
2534
|
-
error: `Cannot coerce ${typeof rawValue} to url_list for field '${fieldId}'`
|
|
2535
|
-
};
|
|
2536
|
-
}
|
|
2537
|
-
function coerceToDate(fieldId, rawValue) {
|
|
2538
|
-
if (rawValue === null) return {
|
|
2539
|
-
ok: true,
|
|
2540
|
-
patch: {
|
|
2541
|
-
op: "set_date",
|
|
2542
|
-
fieldId,
|
|
2543
|
-
value: null
|
|
2544
|
-
}
|
|
2545
|
-
};
|
|
2546
|
-
if (typeof rawValue === "string") return {
|
|
2547
|
-
ok: true,
|
|
2548
|
-
patch: {
|
|
2549
|
-
op: "set_date",
|
|
2550
|
-
fieldId,
|
|
2551
|
-
value: rawValue
|
|
2552
|
-
}
|
|
2553
|
-
};
|
|
2554
|
-
return {
|
|
2555
|
-
ok: false,
|
|
2556
|
-
error: `Cannot coerce ${typeof rawValue} to date for field '${fieldId}'`
|
|
2557
|
-
};
|
|
2558
|
-
}
|
|
2559
|
-
function coerceToYear(fieldId, rawValue) {
|
|
2560
|
-
if (rawValue === null) return {
|
|
2561
|
-
ok: true,
|
|
2562
|
-
patch: {
|
|
2563
|
-
op: "set_year",
|
|
2564
|
-
fieldId,
|
|
2565
|
-
value: null
|
|
2566
|
-
}
|
|
2567
|
-
};
|
|
2568
|
-
if (typeof rawValue === "number") {
|
|
2569
|
-
if (!Number.isInteger(rawValue)) return {
|
|
2570
|
-
ok: false,
|
|
2571
|
-
error: `Year must be an integer for field '${fieldId}', got ${rawValue}`
|
|
2572
|
-
};
|
|
2573
|
-
return {
|
|
2574
|
-
ok: true,
|
|
2575
|
-
patch: {
|
|
2576
|
-
op: "set_year",
|
|
2577
|
-
fieldId,
|
|
2578
|
-
value: rawValue
|
|
2579
|
-
}
|
|
2580
|
-
};
|
|
2581
|
-
}
|
|
2582
|
-
if (typeof rawValue === "string") {
|
|
2583
|
-
const parsed = Number.parseInt(rawValue, 10);
|
|
2584
|
-
if (Number.isNaN(parsed)) return {
|
|
2585
|
-
ok: false,
|
|
2586
|
-
error: `Cannot coerce non-numeric string '${rawValue}' to year for field '${fieldId}'`
|
|
2587
|
-
};
|
|
2588
|
-
return {
|
|
2589
|
-
ok: true,
|
|
2590
|
-
patch: {
|
|
2591
|
-
op: "set_year",
|
|
2592
|
-
fieldId,
|
|
2593
|
-
value: parsed
|
|
2594
|
-
},
|
|
2595
|
-
warning: `Coerced string '${rawValue}' to year for field '${fieldId}'`
|
|
2596
|
-
};
|
|
2597
|
-
}
|
|
2598
|
-
return {
|
|
2599
|
-
ok: false,
|
|
2600
|
-
error: `Cannot coerce ${typeof rawValue} to year for field '${fieldId}'`
|
|
2601
|
-
};
|
|
2602
|
-
}
|
|
2603
|
-
/**
|
|
2604
|
-
* Coerce raw value to SetTablePatch.
|
|
2605
|
-
* Accepts:
|
|
2606
|
-
* - Array of row objects: [{ col1: value1, col2: value2 }, ...]
|
|
2607
|
-
* - Empty array: [] (valid for optional tables or minRows=0)
|
|
2608
|
-
*/
|
|
2609
|
-
function coerceToTable(fieldId, rawValue) {
|
|
2610
|
-
if (rawValue === null) return {
|
|
2611
|
-
ok: true,
|
|
2612
|
-
patch: {
|
|
2613
|
-
op: "set_table",
|
|
2614
|
-
fieldId,
|
|
2615
|
-
value: []
|
|
2616
|
-
}
|
|
2617
|
-
};
|
|
2618
|
-
if (!Array.isArray(rawValue)) return {
|
|
2619
|
-
ok: false,
|
|
2620
|
-
error: `Table value for field '${fieldId}' must be an array of rows, got ${typeof rawValue}`
|
|
2621
|
-
};
|
|
2622
|
-
if (rawValue.length === 0) return {
|
|
2623
|
-
ok: true,
|
|
2624
|
-
patch: {
|
|
2625
|
-
op: "set_table",
|
|
2626
|
-
fieldId,
|
|
2627
|
-
value: []
|
|
2628
|
-
}
|
|
2629
|
-
};
|
|
2630
|
-
const rows = [];
|
|
2631
|
-
for (let i = 0; i < rawValue.length; i++) {
|
|
2632
|
-
const row = rawValue[i];
|
|
2633
|
-
if (typeof row !== "object" || row === null || Array.isArray(row)) return {
|
|
2634
|
-
ok: false,
|
|
2635
|
-
error: `Row ${i} for table field '${fieldId}' must be an object, got ${Array.isArray(row) ? "array" : typeof row}`
|
|
2636
|
-
};
|
|
2637
|
-
rows.push(row);
|
|
2638
|
-
}
|
|
2639
|
-
return {
|
|
2640
|
-
ok: true,
|
|
2641
|
-
patch: {
|
|
2642
|
-
op: "set_table",
|
|
2643
|
-
fieldId,
|
|
2644
|
-
value: rows
|
|
2645
|
-
}
|
|
2646
|
-
};
|
|
2647
|
-
}
|
|
2648
|
-
/**
|
|
2649
|
-
* Coerce a raw value to a Patch for a specific field.
|
|
2650
|
-
*/
|
|
2651
|
-
function coerceToFieldPatch(form, fieldId, rawValue) {
|
|
2652
|
-
const field = findFieldById(form, fieldId);
|
|
2653
|
-
if (!field) return {
|
|
2654
|
-
ok: false,
|
|
2655
|
-
error: `Field '${fieldId}' not found`
|
|
2656
|
-
};
|
|
2657
|
-
switch (field.kind) {
|
|
2658
|
-
case "string": return coerceToString(fieldId, rawValue);
|
|
2659
|
-
case "number": return coerceToNumber(fieldId, rawValue);
|
|
2660
|
-
case "string_list": return coerceToStringList(fieldId, rawValue);
|
|
2661
|
-
case "single_select": return coerceToSingleSelect(field, rawValue);
|
|
2662
|
-
case "multi_select": return coerceToMultiSelect(field, rawValue);
|
|
2663
|
-
case "checkboxes": return coerceToCheckboxes(field, rawValue);
|
|
2664
|
-
case "url": return coerceToUrl(fieldId, rawValue);
|
|
2665
|
-
case "url_list": return coerceToUrlList(fieldId, rawValue);
|
|
2666
|
-
case "date": return coerceToDate(fieldId, rawValue);
|
|
2667
|
-
case "year": return coerceToYear(fieldId, rawValue);
|
|
2668
|
-
case "table": return coerceToTable(fieldId, rawValue);
|
|
2669
|
-
default: {
|
|
2670
|
-
const _exhaustive = field;
|
|
2671
|
-
throw new Error(`Unhandled field kind: ${_exhaustive.kind}`);
|
|
2672
|
-
}
|
|
2673
|
-
}
|
|
2674
|
-
}
|
|
2675
|
-
/**
|
|
2676
|
-
* Coerce an entire InputContext to patches.
|
|
2677
|
-
*
|
|
2678
|
-
* Returns patches for valid entries, collects warnings for coercions,
|
|
2679
|
-
* and errors for invalid entries.
|
|
2680
|
-
*/
|
|
2681
|
-
function coerceInputContext(form, inputContext) {
|
|
2682
|
-
const patches = [];
|
|
2683
|
-
const warnings = [];
|
|
2684
|
-
const errors = [];
|
|
2685
|
-
for (const [fieldId, rawValue] of Object.entries(inputContext)) {
|
|
2686
|
-
if (rawValue === null) continue;
|
|
2687
|
-
const result = coerceToFieldPatch(form, fieldId, rawValue);
|
|
2688
|
-
if (result.ok) {
|
|
2689
|
-
patches.push(result.patch);
|
|
2690
|
-
if ("warning" in result && result.warning) warnings.push(result.warning);
|
|
2691
|
-
} else errors.push(result.error);
|
|
2692
|
-
}
|
|
2693
|
-
return {
|
|
2694
|
-
patches,
|
|
2695
|
-
warnings,
|
|
2696
|
-
errors
|
|
2697
|
-
};
|
|
2698
|
-
}
|
|
2699
|
-
|
|
2700
2157
|
//#endregion
|
|
2701
2158
|
//#region src/harness/harness.ts
|
|
2702
2159
|
/**
|
|
@@ -2818,6 +2275,7 @@ var FormHarness = class {
|
|
|
2818
2275
|
const stepResult = this.computeStepResult(result);
|
|
2819
2276
|
stepResult.patchesApplied = patchesActuallyApplied;
|
|
2820
2277
|
stepResult.rejectedPatches = applyResult.rejectedPatches;
|
|
2278
|
+
stepResult.coercionWarnings = applyResult.warnings;
|
|
2821
2279
|
this.recordTurn(issues, patches, result, llmStats, context, applyResult.rejectedPatches, applyResult.warnings, wire);
|
|
2822
2280
|
if (stepResult.issues.length === 0 || this.turnNumber >= this.config.maxTurns) this.state = "complete";
|
|
2823
2281
|
else this.state = "wait";
|
|
@@ -2828,8 +2286,7 @@ var FormHarness = class {
|
|
|
2828
2286
|
* Applies issue filtering and computes step budget.
|
|
2829
2287
|
*/
|
|
2830
2288
|
computeStepResult(result) {
|
|
2831
|
-
const
|
|
2832
|
-
const limitedIssues = this.filterIssuesByScope(orderFiltered).slice(0, this.config.maxIssuesPerTurn);
|
|
2289
|
+
const limitedIssues = filterIssuesByScope(filterIssuesByOrder(result.issues, this.form), this.form, this.config.maxFieldsPerTurn, this.config.maxGroupsPerTurn).slice(0, this.config.maxIssuesPerTurn);
|
|
2833
2290
|
const stepBudget = Math.min(this.config.maxPatchesPerTurn, limitedIssues.length);
|
|
2834
2291
|
return {
|
|
2835
2292
|
structureSummary: result.structureSummary,
|
|
@@ -2885,91 +2342,6 @@ var FormHarness = class {
|
|
|
2885
2342
|
return sha256(serializeForm(this.form));
|
|
2886
2343
|
}
|
|
2887
2344
|
/**
|
|
2888
|
-
* Filter issues based on maxFieldsPerTurn and maxGroupsPerTurn limits.
|
|
2889
|
-
*
|
|
2890
|
-
* Issues are processed in priority order. An issue is included if:
|
|
2891
|
-
* - Adding it doesn't exceed the field limit (for field/option scoped issues)
|
|
2892
|
-
* - Adding it doesn't exceed the group limit
|
|
2893
|
-
*
|
|
2894
|
-
* Form-level issues are always included.
|
|
2895
|
-
*/
|
|
2896
|
-
filterIssuesByScope(issues) {
|
|
2897
|
-
const maxFields = this.config.maxFieldsPerTurn;
|
|
2898
|
-
const maxGroups = this.config.maxGroupsPerTurn;
|
|
2899
|
-
if (maxFields === void 0 && maxGroups === void 0) return issues;
|
|
2900
|
-
const result = [];
|
|
2901
|
-
const seenFields = /* @__PURE__ */ new Set();
|
|
2902
|
-
const seenGroups = /* @__PURE__ */ new Set();
|
|
2903
|
-
for (const issue of issues) {
|
|
2904
|
-
if (issue.scope === "form") {
|
|
2905
|
-
result.push(issue);
|
|
2906
|
-
continue;
|
|
2907
|
-
}
|
|
2908
|
-
const fieldId = this.getFieldIdFromRef(issue.ref, issue.scope);
|
|
2909
|
-
const groupId = fieldId ? this.getGroupForField(fieldId) : void 0;
|
|
2910
|
-
if (maxFields !== void 0 && fieldId) {
|
|
2911
|
-
if (!seenFields.has(fieldId) && seenFields.size >= maxFields) continue;
|
|
2912
|
-
}
|
|
2913
|
-
if (maxGroups !== void 0 && groupId) {
|
|
2914
|
-
if (!seenGroups.has(groupId) && seenGroups.size >= maxGroups) continue;
|
|
2915
|
-
}
|
|
2916
|
-
result.push(issue);
|
|
2917
|
-
if (fieldId) seenFields.add(fieldId);
|
|
2918
|
-
if (groupId) seenGroups.add(groupId);
|
|
2919
|
-
}
|
|
2920
|
-
return result;
|
|
2921
|
-
}
|
|
2922
|
-
/**
|
|
2923
|
-
* Filter issues by order level.
|
|
2924
|
-
*
|
|
2925
|
-
* Only includes issues for fields at the current (lowest incomplete) order level.
|
|
2926
|
-
* Fields at higher order levels are deferred until all lower-order fields are complete.
|
|
2927
|
-
* If no order attributes are used, all issues pass through (all at order 0).
|
|
2928
|
-
*/
|
|
2929
|
-
filterIssuesByOrder(issues) {
|
|
2930
|
-
const fieldOrderMap = /* @__PURE__ */ new Map();
|
|
2931
|
-
for (const group of this.form.schema.groups) {
|
|
2932
|
-
const groupOrder = group.order ?? 0;
|
|
2933
|
-
for (const field of group.children) fieldOrderMap.set(field.id, field.order ?? groupOrder);
|
|
2934
|
-
}
|
|
2935
|
-
const openOrderLevels = /* @__PURE__ */ new Set();
|
|
2936
|
-
for (const issue of issues) {
|
|
2937
|
-
const fieldId = this.getFieldIdFromRef(issue.ref, issue.scope);
|
|
2938
|
-
if (fieldId) {
|
|
2939
|
-
const order = fieldOrderMap.get(fieldId) ?? 0;
|
|
2940
|
-
openOrderLevels.add(order);
|
|
2941
|
-
} else if (issue.scope === "form") openOrderLevels.add(0);
|
|
2942
|
-
}
|
|
2943
|
-
if (openOrderLevels.size <= 1) return issues;
|
|
2944
|
-
const currentOrder = Math.min(...openOrderLevels);
|
|
2945
|
-
return issues.filter((issue) => {
|
|
2946
|
-
if (issue.scope === "form") return true;
|
|
2947
|
-
const fieldId = this.getFieldIdFromRef(issue.ref, issue.scope);
|
|
2948
|
-
if (!fieldId) return true;
|
|
2949
|
-
return (fieldOrderMap.get(fieldId) ?? 0) === currentOrder;
|
|
2950
|
-
});
|
|
2951
|
-
}
|
|
2952
|
-
/**
|
|
2953
|
-
* Extract field ID from an issue ref.
|
|
2954
|
-
*/
|
|
2955
|
-
getFieldIdFromRef(ref, scope) {
|
|
2956
|
-
if (scope === "field") return ref;
|
|
2957
|
-
if (scope === "option") {
|
|
2958
|
-
const dotIndex = ref.indexOf(".");
|
|
2959
|
-
return dotIndex > 0 ? ref.slice(0, dotIndex) : void 0;
|
|
2960
|
-
}
|
|
2961
|
-
}
|
|
2962
|
-
/**
|
|
2963
|
-
* Get the parent group ID for a field.
|
|
2964
|
-
*/
|
|
2965
|
-
getGroupForField(fieldId) {
|
|
2966
|
-
const entry = this.form.idIndex.get(fieldId);
|
|
2967
|
-
if (!entry) return;
|
|
2968
|
-
if (entry.parentId) {
|
|
2969
|
-
if (this.form.idIndex.get(entry.parentId)?.nodeType === "group") return entry.parentId;
|
|
2970
|
-
}
|
|
2971
|
-
}
|
|
2972
|
-
/**
|
|
2973
2345
|
* Clear all fields that match the target roles.
|
|
2974
2346
|
* Used when fillMode='overwrite' to re-fill already-filled fields.
|
|
2975
2347
|
*/
|
|
@@ -3310,6 +2682,7 @@ var FillRecordCollector = class {
|
|
|
3310
2682
|
});
|
|
3311
2683
|
}
|
|
3312
2684
|
onTurnComplete(progress) {
|
|
2685
|
+
const warnings = progress.coercionWarnings;
|
|
3313
2686
|
this.events.push({
|
|
3314
2687
|
type: "turn_complete",
|
|
3315
2688
|
timestamp: currentTime(),
|
|
@@ -3317,6 +2690,7 @@ var FillRecordCollector = class {
|
|
|
3317
2690
|
patchesApplied: progress.patchesApplied,
|
|
3318
2691
|
patchesRejected: progress.rejectedPatches?.length ?? 0,
|
|
3319
2692
|
issuesAddressed: progress.issuesShown,
|
|
2693
|
+
...warnings && warnings.length > 0 && { coercionWarnings: warnings },
|
|
3320
2694
|
executionId: progress.executionId
|
|
3321
2695
|
});
|
|
3322
2696
|
}
|
|
@@ -3524,7 +2898,8 @@ var FillRecordCollector = class {
|
|
|
3524
2898
|
patchesApplied: completeEvent.patchesApplied,
|
|
3525
2899
|
patchesRejected: completeEvent.patchesRejected,
|
|
3526
2900
|
tokens,
|
|
3527
|
-
toolCalls
|
|
2901
|
+
toolCalls,
|
|
2902
|
+
...completeEvent.coercionWarnings && completeEvent.coercionWarnings.length > 0 && { coercionWarnings: completeEvent.coercionWarnings }
|
|
3528
2903
|
};
|
|
3529
2904
|
turns.set(key, entry);
|
|
3530
2905
|
}
|
|
@@ -8925,186 +8300,6 @@ function createAnthropic(options = {}) {
|
|
|
8925
8300
|
}
|
|
8926
8301
|
var anthropic = createAnthropic();
|
|
8927
8302
|
|
|
8928
|
-
//#endregion
|
|
8929
|
-
//#region src/harness/prompts.ts
|
|
8930
|
-
/**
|
|
8931
|
-
* Agent Prompts - Centralized prompt definitions for the live agent.
|
|
8932
|
-
*
|
|
8933
|
-
* All hardcoded prompts are defined here for easy review, modification,
|
|
8934
|
-
* and future configurability. This file serves as the single source of
|
|
8935
|
-
* truth for agent behavior instructions.
|
|
8936
|
-
*/
|
|
8937
|
-
/**
|
|
8938
|
-
* Default system prompt for the live agent.
|
|
8939
|
-
*
|
|
8940
|
-
* This is the base instruction set that defines the agent's core behavior
|
|
8941
|
-
* for form filling. It emphasizes accuracy over completeness and prohibits
|
|
8942
|
-
* fabrication of data.
|
|
8943
|
-
*/
|
|
8944
|
-
const DEFAULT_SYSTEM_PROMPT = `# Form Instructions
|
|
8945
|
-
|
|
8946
|
-
Research and fill the form fields using all available tools. Focus on accuracy over completeness.
|
|
8947
|
-
|
|
8948
|
-
## Guidelines
|
|
8949
|
-
1. Address required fields first (severity: "required"), then optional fields (severity: "recommended")
|
|
8950
|
-
2. NEVER fabricate or guess information - only use data you can verify
|
|
8951
|
-
3. If you cannot find verifiable information, use skip_field with a reason
|
|
8952
|
-
|
|
8953
|
-
## Patch Format Examples
|
|
8954
|
-
|
|
8955
|
-
Use the fill_form tool with patches in these formats:
|
|
8956
|
-
|
|
8957
|
-
| Type | Example |
|
|
8958
|
-
|------|---------|
|
|
8959
|
-
| string | \`{ op: "set_string", fieldId: "name", value: "Acme Corp" }\` |
|
|
8960
|
-
| number | \`{ op: "set_number", fieldId: "age", value: 32 }\` |
|
|
8961
|
-
| string_list | \`{ op: "set_string_list", fieldId: "tags", value: ["ai", "ml"] }\` |
|
|
8962
|
-
| url | \`{ op: "set_url", fieldId: "website", value: "https://example.com" }\` |
|
|
8963
|
-
| url_list | \`{ op: "set_url_list", fieldId: "sources", value: ["https://a.com", "https://b.com"] }\` |
|
|
8964
|
-
| date | \`{ op: "set_date", fieldId: "event_date", value: "2024-06-15" }\` |
|
|
8965
|
-
| year | \`{ op: "set_year", fieldId: "founded", value: 2024 }\` |
|
|
8966
|
-
| single_select | \`{ op: "set_single_select", fieldId: "priority", value: "high" }\` |
|
|
8967
|
-
| multi_select | \`{ op: "set_multi_select", fieldId: "categories", value: ["frontend", "backend"] }\` |
|
|
8968
|
-
| checkboxes | \`{ op: "set_checkboxes", fieldId: "tasks", value: { "task1": "done", "task2": "todo" } }\` |
|
|
8969
|
-
| table | \`{ op: "set_table", fieldId: "team", value: [{ "name": "Alice", "role": "Engineer" }] }\` |
|
|
8970
|
-
|
|
8971
|
-
## Important: checkboxes vs multi_select
|
|
8972
|
-
|
|
8973
|
-
These two types look similar but have DIFFERENT value formats:
|
|
8974
|
-
|
|
8975
|
-
- **multi_select** → array of option IDs: \`["opt1", "opt2"]\`
|
|
8976
|
-
- **checkboxes** → object mapping IDs to states: \`{ "opt1": "done", "opt2": "todo" }\`
|
|
8977
|
-
|
|
8978
|
-
**Checkbox states by mode:**
|
|
8979
|
-
- Mode "simple": \`"done"\` or \`"todo"\`
|
|
8980
|
-
- Mode "multi": \`"done"\`, \`"todo"\`, \`"incomplete"\`, \`"active"\`, or \`"na"\`
|
|
8981
|
-
- Mode "explicit": \`"yes"\` or \`"no"\` (if unknown, use abort_field)
|
|
8982
|
-
|
|
8983
|
-
**WRONG:** \`{ op: "set_checkboxes", value: ["task1", "task2"] }\`
|
|
8984
|
-
**RIGHT:** \`{ op: "set_checkboxes", value: { "task1": "done", "task2": "done" } }\`
|
|
8985
|
-
|
|
8986
|
-
## Skipping Fields
|
|
8987
|
-
|
|
8988
|
-
If you cannot find verifiable information:
|
|
8989
|
-
\`{ op: "skip_field", fieldId: "...", reason: "Could not find verified data" }\`
|
|
8990
|
-
`;
|
|
8991
|
-
/**
|
|
8992
|
-
* Web search instructions appended when web search tools are available.
|
|
8993
|
-
*
|
|
8994
|
-
* These instructions enforce that the agent must verify all information
|
|
8995
|
-
* through web search before filling fields.
|
|
8996
|
-
*/
|
|
8997
|
-
const WEB_SEARCH_INSTRUCTIONS = `# Web Search
|
|
8998
|
-
You have access to web search tools. You MUST use them to verify ALL information before filling fields.
|
|
8999
|
-
|
|
9000
|
-
Guidelines:
|
|
9001
|
-
1. Search for official sources (company websites, Crunchbase, LinkedIn, press releases)
|
|
9002
|
-
2. Cross-reference information across multiple sources when possible
|
|
9003
|
-
3. Only fill fields with data you found and verified through search
|
|
9004
|
-
4. If a search returns no results or uncertain information, use skip_field with a reason explaining what you searched for
|
|
9005
|
-
5. NEVER fill fields with guessed or assumed information
|
|
9006
|
-
`;
|
|
9007
|
-
/**
|
|
9008
|
-
* Header for the issues section in the context prompt.
|
|
9009
|
-
*/
|
|
9010
|
-
const ISSUES_HEADER = "# Current Form Issues";
|
|
9011
|
-
/**
|
|
9012
|
-
* Template for the issues intro text.
|
|
9013
|
-
* @param issueCount - Actual number of issues shown
|
|
9014
|
-
*/
|
|
9015
|
-
function getIssuesIntro(issueCount) {
|
|
9016
|
-
return `You need to address ${issueCount} issue${issueCount === 1 ? "" : "s"}. Here are the current issues:`;
|
|
9017
|
-
}
|
|
9018
|
-
/**
|
|
9019
|
-
* Patch format examples by field kind.
|
|
9020
|
-
*
|
|
9021
|
-
* This is the single source of truth for patch format documentation.
|
|
9022
|
-
* Used in PATCH_FORMAT_INSTRUCTIONS and rejection feedback hints.
|
|
9023
|
-
*/
|
|
9024
|
-
const PATCH_FORMATS = {
|
|
9025
|
-
string: "{ op: \"set_string\", fieldId: \"...\", value: \"text here\" }",
|
|
9026
|
-
number: "{ op: \"set_number\", fieldId: \"...\", value: 42 }",
|
|
9027
|
-
string_list: "{ op: \"set_string_list\", fieldId: \"...\", value: [\"item1\", \"item2\"] }",
|
|
9028
|
-
single_select: "{ op: \"set_single_select\", fieldId: \"...\", value: \"option_id\" }",
|
|
9029
|
-
multi_select: "{ op: \"set_multi_select\", fieldId: \"...\", value: [\"opt1\", \"opt2\"] }",
|
|
9030
|
-
checkboxes: "{ op: \"set_checkboxes\", fieldId: \"...\", value: { \"opt1\": \"done\", \"opt2\": \"todo\" } }",
|
|
9031
|
-
url: "{ op: \"set_url\", fieldId: \"...\", value: \"https://example.com\" }",
|
|
9032
|
-
url_list: "{ op: \"set_url_list\", fieldId: \"...\", value: [\"https://a.com\", \"https://b.com\"] }",
|
|
9033
|
-
date: "{ op: \"set_date\", fieldId: \"...\", value: \"2024-06-15\" }",
|
|
9034
|
-
year: "{ op: \"set_year\", fieldId: \"...\", value: 2024 }",
|
|
9035
|
-
table: "{ op: \"set_table\", fieldId: \"...\", value: [{ col1: \"val1\", col2: \"val2\" }] }"
|
|
9036
|
-
};
|
|
9037
|
-
/**
|
|
9038
|
-
* Get the correct patch format for a field kind.
|
|
9039
|
-
*
|
|
9040
|
-
* @param fieldKind - The field kind (e.g., "table", "string")
|
|
9041
|
-
* @param options - Optional configuration for the hint
|
|
9042
|
-
* @returns The patch format example string
|
|
9043
|
-
*/
|
|
9044
|
-
function getPatchFormatHint(fieldKind, fieldIdOrOptions, columnIds) {
|
|
9045
|
-
let options = {};
|
|
9046
|
-
if (typeof fieldIdOrOptions === "string") options = {
|
|
9047
|
-
fieldId: fieldIdOrOptions,
|
|
9048
|
-
columnIds
|
|
9049
|
-
};
|
|
9050
|
-
else if (fieldIdOrOptions) options = fieldIdOrOptions;
|
|
9051
|
-
let format = PATCH_FORMATS[fieldKind];
|
|
9052
|
-
if (!format) return `Use the correct set_${fieldKind} operation for this field type.`;
|
|
9053
|
-
if (options.fieldId) format = format.replace("fieldId: \"...\"", `fieldId: "${options.fieldId}"`);
|
|
9054
|
-
if (fieldKind === "checkboxes") {
|
|
9055
|
-
const mode = options.checkboxMode ?? "multi";
|
|
9056
|
-
const optIds = options.optionIds ?? ["opt1", "opt2"];
|
|
9057
|
-
const [state1, state2] = {
|
|
9058
|
-
simple: ["done", "todo"],
|
|
9059
|
-
multi: ["done", "todo"],
|
|
9060
|
-
explicit: ["yes", "no"]
|
|
9061
|
-
}[mode] ?? ["done", "todo"];
|
|
9062
|
-
const valueExample = optIds.length >= 2 ? `{ "${optIds[0]}": "${state1}", "${optIds[1]}": "${state2}" }` : optIds.length === 1 ? `{ "${optIds[0]}": "${state1}" }` : `{ "opt1": "${state1}", "opt2": "${state2}" }`;
|
|
9063
|
-
format = format.replace("{ \"opt1\": \"done\", \"opt2\": \"todo\" }", valueExample);
|
|
9064
|
-
}
|
|
9065
|
-
if (fieldKind === "table" && options.columnIds && options.columnIds.length > 0) {
|
|
9066
|
-
const colExample = options.columnIds.map((id) => `"${id}": "..."`).join(", ");
|
|
9067
|
-
format = format.replace("{ col1: \"val1\", col2: \"val2\" }", `{ ${colExample} }`);
|
|
9068
|
-
}
|
|
9069
|
-
return format;
|
|
9070
|
-
}
|
|
9071
|
-
/**
|
|
9072
|
-
* Instructions section for the context prompt.
|
|
9073
|
-
*
|
|
9074
|
-
* This explains the patch format for each field kind.
|
|
9075
|
-
* Generated from PATCH_FORMATS to ensure consistency.
|
|
9076
|
-
*/
|
|
9077
|
-
const PATCH_FORMAT_INSTRUCTIONS = `# Instructions
|
|
9078
|
-
|
|
9079
|
-
Use the fill_form tool to submit patches for the fields above.
|
|
9080
|
-
Each patch should match the field kind:
|
|
9081
|
-
${Object.entries(PATCH_FORMATS).map(([kind, format]) => `- ${kind}: ${format}`).join("\n")}
|
|
9082
|
-
|
|
9083
|
-
For table fields, use the column IDs shown in the field schema. Each row is an object with column ID keys.
|
|
9084
|
-
|
|
9085
|
-
If you cannot find verifiable information for a field, skip it:
|
|
9086
|
-
- skip: { op: "skip_field", fieldId: "...", reason: "Information not available" }`;
|
|
9087
|
-
/**
|
|
9088
|
-
* Simplified general instructions for use with inline field instructions.
|
|
9089
|
-
*
|
|
9090
|
-
* When inline field instructions are shown after each issue, we only need
|
|
9091
|
-
* general guidance about using the fill_form tool.
|
|
9092
|
-
*/
|
|
9093
|
-
const GENERAL_INSTRUCTIONS = `# General Instructions
|
|
9094
|
-
|
|
9095
|
-
Use the fill_form tool to submit patches for the fields above.
|
|
9096
|
-
For table fields, each row is an object with column ID keys.`;
|
|
9097
|
-
/**
|
|
9098
|
-
* Section headers used when building the composed system prompt.
|
|
9099
|
-
*/
|
|
9100
|
-
const SECTION_HEADERS = {
|
|
9101
|
-
formInstructions: "# Form Instructions",
|
|
9102
|
-
roleInstructions: (role) => `# Instructions for ${role} role`,
|
|
9103
|
-
roleGuidance: "# Role guidance",
|
|
9104
|
-
fieldInstructions: "# Field-specific instructions",
|
|
9105
|
-
additionalContext: "# Additional Context"
|
|
9106
|
-
};
|
|
9107
|
-
|
|
9108
8303
|
//#endregion
|
|
9109
8304
|
//#region src/harness/toolApi.ts
|
|
9110
8305
|
/**
|
|
@@ -9135,6 +8330,7 @@ var LiveAgent = class {
|
|
|
9135
8330
|
callbacks;
|
|
9136
8331
|
executionId;
|
|
9137
8332
|
toolChoice;
|
|
8333
|
+
maxRetries;
|
|
9138
8334
|
constructor(config) {
|
|
9139
8335
|
this.model = config.model;
|
|
9140
8336
|
this.maxStepsPerTurn = config.maxStepsPerTurn ?? DEFAULT_MAX_STEPS_PER_TURN;
|
|
@@ -9145,8 +8341,12 @@ var LiveAgent = class {
|
|
|
9145
8341
|
this.additionalTools = config.additionalTools ?? {};
|
|
9146
8342
|
this.callbacks = config.callbacks;
|
|
9147
8343
|
this.executionId = config.executionId ?? "0-serial";
|
|
8344
|
+
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
9148
8345
|
this.toolChoice = config.toolChoice ?? "required";
|
|
9149
|
-
if (this.enableWebSearch
|
|
8346
|
+
if (this.enableWebSearch) {
|
|
8347
|
+
if (config.providerTools) this.webSearchTools = config.providerTools;
|
|
8348
|
+
else if (this.provider) this.webSearchTools = loadWebSearchTools(this.provider);
|
|
8349
|
+
}
|
|
9150
8350
|
}
|
|
9151
8351
|
/**
|
|
9152
8352
|
* Get list of available tool names for this agent.
|
|
@@ -9201,6 +8401,7 @@ var LiveAgent = class {
|
|
|
9201
8401
|
prompt: contextPrompt,
|
|
9202
8402
|
tools,
|
|
9203
8403
|
toolChoice: this.toolChoice,
|
|
8404
|
+
maxRetries: this.maxRetries,
|
|
9204
8405
|
stopWhen: stepCountIs(this.maxStepsPerTurn)
|
|
9205
8406
|
});
|
|
9206
8407
|
} catch (error) {
|
|
@@ -9692,6 +8893,61 @@ const PROVIDERS = {
|
|
|
9692
8893
|
}
|
|
9693
8894
|
};
|
|
9694
8895
|
/**
|
|
8896
|
+
* Built-in providers available by default.
|
|
8897
|
+
* Exported so callers can inspect which providers are built-in.
|
|
8898
|
+
*/
|
|
8899
|
+
const BUILT_IN_PROVIDERS = Object.freeze({
|
|
8900
|
+
anthropic: "anthropic",
|
|
8901
|
+
openai: "openai",
|
|
8902
|
+
google: "google",
|
|
8903
|
+
xai: "xai",
|
|
8904
|
+
deepseek: "deepseek"
|
|
8905
|
+
});
|
|
8906
|
+
/** Known web search tool names from AI SDK providers. */
|
|
8907
|
+
const KNOWN_WEB_SEARCH_TOOLS = [
|
|
8908
|
+
"webSearch",
|
|
8909
|
+
"webSearch_20250305",
|
|
8910
|
+
"googleSearch",
|
|
8911
|
+
"webSearchPreview"
|
|
8912
|
+
];
|
|
8913
|
+
/**
|
|
8914
|
+
* Extract web search tools from an AI SDK provider callable.
|
|
8915
|
+
* Duck-types the `.tools` property looking for known tool factory names.
|
|
8916
|
+
*/
|
|
8917
|
+
function extractToolsFromProvider(provider) {
|
|
8918
|
+
const providerTools = provider.tools;
|
|
8919
|
+
if (!providerTools || typeof providerTools !== "object") return void 0;
|
|
8920
|
+
const extracted = {};
|
|
8921
|
+
for (const toolName of KNOWN_WEB_SEARCH_TOOLS) {
|
|
8922
|
+
const factory = providerTools[toolName];
|
|
8923
|
+
if (typeof factory === "function") try {
|
|
8924
|
+
const tool = factory({});
|
|
8925
|
+
if (tool) {
|
|
8926
|
+
const key = toolName === "googleSearch" ? "google_search" : "web_search";
|
|
8927
|
+
extracted[key] = tool;
|
|
8928
|
+
break;
|
|
8929
|
+
}
|
|
8930
|
+
} catch {}
|
|
8931
|
+
}
|
|
8932
|
+
return Object.keys(extracted).length > 0 ? extracted : void 0;
|
|
8933
|
+
}
|
|
8934
|
+
/**
|
|
8935
|
+
* Normalize a ProviderInput to a ProviderAdapter.
|
|
8936
|
+
* AI SDK provider callables are wrapped in an adapter shape.
|
|
8937
|
+
*/
|
|
8938
|
+
function normalizeProvider(input) {
|
|
8939
|
+
if (typeof input === "function") return {
|
|
8940
|
+
model: (id) => input(id),
|
|
8941
|
+
tools: extractToolsFromProvider(input)
|
|
8942
|
+
};
|
|
8943
|
+
if ("model" in input && typeof input.model === "function") return input;
|
|
8944
|
+
throw new MarkformConfigError("Invalid provider: must be a ProviderAdapter (with .model() method) or an AI SDK provider callable", {
|
|
8945
|
+
option: "providers",
|
|
8946
|
+
expectedType: "ProviderAdapter | callable",
|
|
8947
|
+
receivedValue: typeof input
|
|
8948
|
+
});
|
|
8949
|
+
}
|
|
8950
|
+
/**
|
|
9695
8951
|
* Parse a model ID string into provider and model components.
|
|
9696
8952
|
*
|
|
9697
8953
|
* @param modelIdString - Model ID in format `provider/model-id`
|
|
@@ -9712,12 +8968,6 @@ function parseModelId(modelIdString) {
|
|
|
9712
8968
|
expectedType: "non-empty provider and model-id",
|
|
9713
8969
|
receivedValue: modelIdString
|
|
9714
8970
|
});
|
|
9715
|
-
const supportedProviders = Object.keys(PROVIDERS);
|
|
9716
|
-
if (!supportedProviders.includes(provider)) throw new MarkformConfigError(`Unknown provider: "${provider}". Supported providers: ${supportedProviders.join(", ")}`, {
|
|
9717
|
-
option: "model",
|
|
9718
|
-
expectedType: `one of: ${supportedProviders.join(", ")}`,
|
|
9719
|
-
receivedValue: provider
|
|
9720
|
-
});
|
|
9721
8971
|
return {
|
|
9722
8972
|
provider,
|
|
9723
8973
|
modelId
|
|
@@ -9732,9 +8982,31 @@ function parseModelId(modelIdString) {
|
|
|
9732
8982
|
* @returns Resolved model with provider info
|
|
9733
8983
|
* @throws Error if provider not installed or API key missing
|
|
9734
8984
|
*/
|
|
9735
|
-
async function resolveModel(modelIdString) {
|
|
8985
|
+
async function resolveModel(modelIdString, providers) {
|
|
9736
8986
|
const { provider, modelId } = parseModelId(modelIdString);
|
|
8987
|
+
if (providers && provider in providers) {
|
|
8988
|
+
const adapter = normalizeProvider(providers[provider]);
|
|
8989
|
+
try {
|
|
8990
|
+
return {
|
|
8991
|
+
model: adapter.model(modelId),
|
|
8992
|
+
provider,
|
|
8993
|
+
modelId,
|
|
8994
|
+
tools: adapter.tools
|
|
8995
|
+
};
|
|
8996
|
+
} catch (error) {
|
|
8997
|
+
throw new MarkformConfigError(`Custom provider "${provider}" failed to resolve model "${modelId}": ${error instanceof Error ? error.message : String(error)}`, {
|
|
8998
|
+
option: "model",
|
|
8999
|
+
expectedType: "valid model ID",
|
|
9000
|
+
receivedValue: modelIdString
|
|
9001
|
+
});
|
|
9002
|
+
}
|
|
9003
|
+
}
|
|
9737
9004
|
const providerConfig = PROVIDERS[provider];
|
|
9005
|
+
if (!providerConfig) throw new MarkformConfigError(`Unknown provider: "${provider}". Built-in providers: ${Object.keys(PROVIDERS).join(", ")}. To use a custom provider, pass it via the \`providers\` option.`, {
|
|
9006
|
+
option: "model",
|
|
9007
|
+
expectedType: "provider name or custom provider",
|
|
9008
|
+
receivedValue: provider
|
|
9009
|
+
});
|
|
9738
9010
|
const apiKey = process.env[providerConfig.envVar];
|
|
9739
9011
|
if (!apiKey) throw new MarkformConfigError(`Missing API key for "${provider}" provider (model: ${modelIdString}).\nSet the ${providerConfig.envVar} environment variable or add it to your .env file.`, {
|
|
9740
9012
|
option: providerConfig.envVar,
|
|
@@ -10264,11 +9536,13 @@ async function fillForm(options) {
|
|
|
10264
9536
|
}
|
|
10265
9537
|
let model;
|
|
10266
9538
|
let provider;
|
|
9539
|
+
let providerTools;
|
|
10267
9540
|
if (!options._testAgent) try {
|
|
10268
9541
|
if (typeof options.model === "string") {
|
|
10269
|
-
const resolved = await resolveModel(options.model);
|
|
9542
|
+
const resolved = await resolveModel(options.model, options.providers);
|
|
10270
9543
|
model = resolved.model;
|
|
10271
9544
|
provider = resolved.provider;
|
|
9545
|
+
providerTools = resolved.tools;
|
|
10272
9546
|
} else model = options.model;
|
|
10273
9547
|
} catch (error) {
|
|
10274
9548
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -10310,6 +9584,7 @@ async function fillForm(options) {
|
|
|
10310
9584
|
systemPromptAddition: options.systemPromptAddition,
|
|
10311
9585
|
targetRole: targetRoles[0] ?? AGENT_ROLE,
|
|
10312
9586
|
provider,
|
|
9587
|
+
providerTools,
|
|
10313
9588
|
enableWebSearch: options.enableWebSearch,
|
|
10314
9589
|
additionalTools: options.additionalTools,
|
|
10315
9590
|
callbacks: mergedCallbacks,
|
|
@@ -10426,6 +9701,7 @@ async function fillForm(options) {
|
|
|
10426
9701
|
issues: turnIssues,
|
|
10427
9702
|
patches,
|
|
10428
9703
|
rejectedPatches: stepResult.rejectedPatches ?? [],
|
|
9704
|
+
coercionWarnings: stepResult.coercionWarnings,
|
|
10429
9705
|
executionId: "0-serial"
|
|
10430
9706
|
});
|
|
10431
9707
|
} catch {}
|
|
@@ -10654,10 +9930,12 @@ async function runMultiTurnForItems(form, agent, items, targetRoles, maxPatchesP
|
|
|
10654
9930
|
}
|
|
10655
9931
|
};
|
|
10656
9932
|
}
|
|
9933
|
+
let lastCoercionWarnings;
|
|
10657
9934
|
if (response.patches.length > 0) {
|
|
10658
9935
|
const applyResult = applyPatches(form, response.patches);
|
|
10659
9936
|
patchesApplied += applyResult.appliedPatches.length;
|
|
10660
9937
|
previousRejections = applyResult.rejectedPatches;
|
|
9938
|
+
lastCoercionWarnings = applyResult.warnings;
|
|
10661
9939
|
} else previousRejections = void 0;
|
|
10662
9940
|
turnsUsed++;
|
|
10663
9941
|
try {
|
|
@@ -10673,6 +9951,7 @@ async function runMultiTurnForItems(form, agent, items, targetRoles, maxPatchesP
|
|
|
10673
9951
|
issues: scopedIssues,
|
|
10674
9952
|
patches: response.patches,
|
|
10675
9953
|
rejectedPatches: previousRejections ?? [],
|
|
9954
|
+
coercionWarnings: lastCoercionWarnings,
|
|
10676
9955
|
executionId
|
|
10677
9956
|
});
|
|
10678
9957
|
} catch {}
|
|
@@ -10866,7 +10145,8 @@ const TimelineEntrySchema = z.object({
|
|
|
10866
10145
|
input: z.number().int().nonnegative(),
|
|
10867
10146
|
output: z.number().int().nonnegative()
|
|
10868
10147
|
}),
|
|
10869
|
-
toolCalls: z.array(ToolCallRecordSchema)
|
|
10148
|
+
toolCalls: z.array(ToolCallRecordSchema),
|
|
10149
|
+
coercionWarnings: z.array(PatchWarningSchema).optional()
|
|
10870
10150
|
});
|
|
10871
10151
|
/**
|
|
10872
10152
|
* Breakdown item for visualization.
|
|
@@ -10968,6 +10248,14 @@ const FillRecordSchema = z.object({
|
|
|
10968
10248
|
* - toolSummary: call counts and success rates (without timing)
|
|
10969
10249
|
* - execution: turn counts, parallel settings (deterministic)
|
|
10970
10250
|
*/
|
|
10251
|
+
/**
|
|
10252
|
+
* Check if a fill record represents an empty session with no actual work.
|
|
10253
|
+
* Returns true if the timeline has zero entries (no turns were executed).
|
|
10254
|
+
* Used to skip writing .fill.json when no form-filling work was done.
|
|
10255
|
+
*/
|
|
10256
|
+
function isEmptyFillRecord(record) {
|
|
10257
|
+
return record.timeline.length === 0;
|
|
10258
|
+
}
|
|
10971
10259
|
function stripUnstableFillRecordFields(record) {
|
|
10972
10260
|
const stableByTool = record.toolSummary.byTool.map((toolStats) => {
|
|
10973
10261
|
const { timing, ...rest } = toolStats;
|
|
@@ -11111,8 +10399,8 @@ function validateResearchForm(form) {
|
|
|
11111
10399
|
//#endregion
|
|
11112
10400
|
//#region src/index.ts
|
|
11113
10401
|
/** Markform version (injected at build time). */
|
|
11114
|
-
const VERSION = "0.1.
|
|
10402
|
+
const VERSION = "0.1.24";
|
|
11115
10403
|
|
|
11116
10404
|
//#endregion
|
|
11117
|
-
export {
|
|
11118
|
-
//# sourceMappingURL=src-
|
|
10405
|
+
export { MockAgent as A, fieldToJsonSchema as B, getProviderInfo as C, createLiveAgent as D, buildMockWireFormat as E, isCellRef as F, injectHeaderIds as G, parseForm as H, isFieldRef as I, parseCellValue as J, findAllHeadings as K, isQualifiedRef as L, FormHarness as M, createHarness as N, FillRecordCollector as O, getFieldId as P, parseScopeRef as R, BUILT_IN_PROVIDERS as S, resolveModel as T, findAllCheckboxes as U, formToJsonSchema as V, injectCheckboxIds as W, parseRawTable as X, parseMarkdownTable as Y, resolveHarnessConfig as _, ExecutionMetadataSchema as a, createParallelHarness as b, TimelineEntrySchema as c, ToolCallRecordSchema as d, ToolStatsSchema as f, formatFillRecordSummary as g, stripUnstableFillRecordFields as h, runResearch as i, createMockAgent as j, computeExecutionPlan as k, TimingBreakdownItemSchema as l, isEmptyFillRecord as m, isResearchForm as n, FillRecordSchema as o, ToolSummarySchema as p, findEnclosingHeadings as q, validateResearchForm as r, FillRecordStatusSchema as s, VERSION as t, TimingBreakdownSchema as u, fillForm as v, getProviderNames as w, scopeIssuesForItem as x, ParallelHarness as y, serializeScopeRef as z };
|
|
10406
|
+
//# sourceMappingURL=src-C5OWf1dL.mjs.map
|