markform 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -18
- package/dist/ai-sdk.d.mts +1 -1
- package/dist/ai-sdk.mjs +1 -1
- package/dist/{apply-CD-t7ovb.mjs → apply-KzQztrDV.mjs} +100 -74
- package/dist/apply-KzQztrDV.mjs.map +1 -0
- package/dist/bin.mjs +1 -1
- package/dist/{cli-ChdIy1a7.mjs → cli-ZcOC47KK.mjs} +24 -1213
- package/dist/cli-ZcOC47KK.mjs.map +1 -0
- package/dist/cli.mjs +1 -1
- package/dist/{coreTypes-BQrWf_Wt.d.mts → coreTypes-BlsJkU1w.d.mts} +1 -1
- package/dist/fillRecord-DTl5lnK0.d.mts +345 -0
- package/dist/fillRecordRenderer-VBQ2vwPV.mjs +1253 -0
- package/dist/fillRecordRenderer-VBQ2vwPV.mjs.map +1 -0
- package/dist/index.d.mts +53 -343
- package/dist/index.mjs +4 -4
- package/dist/render.d.mts +74 -0
- package/dist/render.mjs +4 -0
- package/dist/{session-ZgegwtkT.mjs → session-BCcltrLA.mjs} +1 -1
- package/dist/{session-ZgegwtkT.mjs.map → session-BCcltrLA.mjs.map} +1 -1
- package/dist/{session-BPuQ-ok0.mjs → session-VeSkVrck.mjs} +1 -1
- package/dist/{shared-DwdyWmvE.mjs → shared-CsdT2T7k.mjs} +1 -1
- package/dist/{shared-DwdyWmvE.mjs.map → shared-CsdT2T7k.mjs.map} +1 -1
- package/dist/{shared-BTR35aMz.mjs → shared-fb0nkzQi.mjs} +1 -1
- package/dist/{src-DOPe4tmu.mjs → src-B2uFvGli.mjs} +103 -21
- package/dist/{src-DOPe4tmu.mjs.map → src-B2uFvGli.mjs.map} +1 -1
- package/dist/urlFormat-lls7CsEP.mjs +71 -0
- package/dist/urlFormat-lls7CsEP.mjs.map +1 -0
- package/docs/markform-apis.md +53 -0
- package/examples/simple/simple-skipped-filled.report.md +8 -8
- package/examples/twitter-thread/twitter-thread.form.md +373 -0
- package/package.json +5 -1
- package/dist/apply-CD-t7ovb.mjs.map +0 -1
- package/dist/cli-ChdIy1a7.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# Markform
|
|
2
2
|
|
|
3
|
-
[](https://github.com/jlevy/markform/actions/runs/
|
|
4
|
-
[](https://github.com/jlevy/markform/actions/runs/
|
|
3
|
+
[](https://github.com/jlevy/markform/actions/runs/21980795343)
|
|
4
|
+
[](https://github.com/jlevy/markform/actions/runs/21980795343)
|
|
5
5
|
[](https://www.npmjs.com/package/markform)
|
|
6
6
|
[](https://x.com/ojoshe)
|
|
7
7
|
|
|
8
|
+
### What if your Markdown docs had an agent-friendly semantic API?
|
|
9
|
+
|
|
8
10
|
**Markform** is a text format for defining structured forms that humans can read,
|
|
9
11
|
machines can parse, and agents can fill via tool calls.
|
|
10
12
|
|
|
@@ -13,12 +15,14 @@ Agents fill forms incrementally via patches.
|
|
|
13
15
|
Fields are validated, so errors are caught early and can be corrected.
|
|
14
16
|
Humans can review or intervene at any point.
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
### Why forms?
|
|
19
|
+
|
|
20
|
+
For deep research or complex AI tasks, you need more than just prompts or
|
|
17
21
|
flow: you need *structure*, which is precise control over agent output at every stage of
|
|
18
22
|
a workflow. A well-designed form combines instructions, structured data, and validations
|
|
19
23
|
in one place.
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
### How it Works
|
|
22
26
|
|
|
23
27
|
- A Markform document exposes a programmatic interface: users fill fields via CLI or web
|
|
24
28
|
UI, agents fill via tool calls ([Vercel AI SDK](https://github.com/vercel/ai)
|
|
@@ -33,9 +37,25 @@ in one place.
|
|
|
33
37
|
[precise specification](https://github.com/jlevy/markform/blob/main/docs/markform-spec.md).
|
|
34
38
|
Export Markform syntax to JSON, YAML, JSON Schema, or plain Markdown reports.
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
- Optionally, the whole thing is wrapped in a harness where large forms can be filled
|
|
41
|
+
concurrently by any LLM in a structured agentic loop.
|
|
42
|
+
|
|
43
|
+
### Useful details
|
|
44
|
+
|
|
45
|
+
- Markform syntax is a good source format: it is **token-efficient text** you can read, diff, and
|
|
46
|
+
version control and it is **ideal for context engineering** because it combines
|
|
47
|
+
document context, data schema, and memory (data filled so far).
|
|
48
|
+
|
|
49
|
+
- Structure is defined with HTML comment tags (`<!-- field -->`) that
|
|
50
|
+
render invisibly on GitHub, so **forms look like regular Markdown**. (Jinja-style
|
|
51
|
+
tag syntax also works if you prefer.)
|
|
52
|
+
|
|
53
|
+
- Optionally, **a fill record** of the form-filling process is kept, so you can see
|
|
54
|
+
and debug exactly how forms are filled by agents, tool usage, LLM call time, etc.
|
|
55
|
+
|
|
56
|
+
- The CLI has a built-in web renderer, **`markform serve`**, for easy viewing and debugging
|
|
57
|
+
of forms (including a form web UI, the form schema, and a waterfall-style overview of the
|
|
58
|
+
fill record, including performance details, which is useful for large, concurrently filled forms).
|
|
39
59
|
|
|
40
60
|
## Simple Example: Research a Movie
|
|
41
61
|
|
|
@@ -364,16 +384,16 @@ flowchart LR
|
|
|
364
384
|
subgraph SPEC["<b>MARKFORM SPEC</b>"]
|
|
365
385
|
direction TB
|
|
366
386
|
|
|
367
|
-
subgraph L1["<b>LAYER 1: SYNTAX</b><br/>Markdoc tag syntax
|
|
387
|
+
subgraph L1["<b>LAYER 1: SYNTAX</b><br/>Markdoc tag syntax<br/>and frontmatter (form,<br/>group, string-field, <br/>checkboxes, etc.)"]
|
|
368
388
|
end
|
|
369
389
|
|
|
370
|
-
subgraph L2["<b>LAYER 2: FORM DATA MODEL</b><br/>Schema definitions
|
|
390
|
+
subgraph L2["<b>LAYER 2: FORM DATA MODEL</b><br/>Schema definitions<br/>for forms, fields, values"]
|
|
371
391
|
end
|
|
372
392
|
|
|
373
|
-
subgraph L3["<b>LAYER 3: VALIDATION
|
|
393
|
+
subgraph L3["<b>LAYER 3: VALIDATION <br/>AND PATCHES</b><br/>Rules for filling forms<br/>via patches, required<br/>field semantics, validation"]
|
|
374
394
|
end
|
|
375
395
|
|
|
376
|
-
subgraph L4["<b>LAYER 4: TOOL API
|
|
396
|
+
subgraph L4["<b>LAYER 4: TOOL API <br/>AND INTERFACES</b><br/>Abstract form-filling<br/>loop, concurrency<br/>model, tool layer"]
|
|
377
397
|
end
|
|
378
398
|
|
|
379
399
|
L4 --> L3 --> L2 --> L1
|
|
@@ -382,19 +402,19 @@ flowchart LR
|
|
|
382
402
|
subgraph IMPL["<b>THIS IMPLEMENTATION</b>"]
|
|
383
403
|
direction TB
|
|
384
404
|
|
|
385
|
-
subgraph CLI["<b>`markform` CLI</b><br/>Command-line interface
|
|
405
|
+
subgraph CLI["<b>`markform` CLI</b><br/>Command-line interface<br/>to all features"]
|
|
386
406
|
end
|
|
387
407
|
|
|
388
|
-
subgraph AGENT["<b>AGENT TOOL INTERFACE</b><br/>Tool API library
|
|
408
|
+
subgraph AGENT["<b>AGENT TOOL INTERFACE</b><br/>Tool API library><br/>(AI SDK tools)"]
|
|
389
409
|
end
|
|
390
410
|
|
|
391
|
-
subgraph HARNESS["<b>EXECUTION HARNESS</b><br/>
|
|
411
|
+
subgraph HARNESS["<b>EXECUTION HARNESS</b><br/>Concurrent form-filling<br/>agentic loop<br/>(AI SDK)"]
|
|
392
412
|
end
|
|
393
413
|
|
|
394
|
-
subgraph ENGINE["<b>CORE TYPESCRIPT APIS</b><br/>Markdoc parser, serializer
|
|
414
|
+
subgraph ENGINE["<b>CORE TYPESCRIPT APIS</b><br/>Markdoc parser, serializer,<br/>patch application,<br/>validation (jiti for rules)"]
|
|
395
415
|
end
|
|
396
416
|
|
|
397
|
-
subgraph TEST["<b>TESTING FRAMEWORK</b><br/>Golden session testing
|
|
417
|
+
subgraph TEST["<b>TESTING FRAMEWORK</b><br/>Golden session testing<br/>(.session.yaml transcripts)"]
|
|
398
418
|
end
|
|
399
419
|
|
|
400
420
|
CLI --> ENGINE
|
|
@@ -551,8 +571,8 @@ If unsure, try `gpt-5-mini` first as it’s fast and supports web search.
|
|
|
551
571
|
|
|
552
572
|
## Programmatic Usage
|
|
553
573
|
|
|
554
|
-
Markform exports a parsing engine and AI SDK integration for use in
|
|
555
|
-
applications.
|
|
574
|
+
Markform exports a parsing engine, rendering functions, and AI SDK integration for use in
|
|
575
|
+
your own applications.
|
|
556
576
|
|
|
557
577
|
### Basic Parsing
|
|
558
578
|
|
|
@@ -594,6 +614,34 @@ See the
|
|
|
594
614
|
[API documentation](https://github.com/jlevy/markform/blob/main/docs/markform-apis.md)
|
|
595
615
|
for options like parallel execution, callbacks, and checkpointing.
|
|
596
616
|
|
|
617
|
+
### Rendering API
|
|
618
|
+
|
|
619
|
+
Import from the `markform/render` subpath to render forms and fill records as HTML
|
|
620
|
+
fragments — the same output as `markform serve`, without pulling in CLI/server
|
|
621
|
+
dependencies:
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
import {
|
|
625
|
+
renderViewContent,
|
|
626
|
+
renderFillRecordContent,
|
|
627
|
+
FILL_RECORD_STYLES,
|
|
628
|
+
FILL_RECORD_SCRIPTS,
|
|
629
|
+
} from "markform/render";
|
|
630
|
+
|
|
631
|
+
// Render a filled form as read-only HTML
|
|
632
|
+
const formHtml = renderViewContent(parsedForm);
|
|
633
|
+
|
|
634
|
+
// Render a fill record dashboard
|
|
635
|
+
const dashboardHtml = renderFillRecordContent(fillRecord);
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
Also exports `renderSourceContent`, `renderMarkdownContent`, `renderYamlContent`,
|
|
639
|
+
`renderJsonContent`, `escapeHtml`, `formatDuration`, and `formatTokens`.
|
|
640
|
+
|
|
641
|
+
See the
|
|
642
|
+
[API documentation](https://github.com/jlevy/markform/blob/main/docs/markform-apis.md#rendering-api)
|
|
643
|
+
for full details.
|
|
644
|
+
|
|
597
645
|
### AI SDK Integration
|
|
598
646
|
|
|
599
647
|
Markform provides tools compatible with the [Vercel AI SDK](https://sdk.vercel.ai/):
|
package/dist/ai-sdk.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { At as ParsedForm, Nt as PatchSchema, U as FieldResponse, Y as FormSchema, at as InspectResult, et as Id, hr as ValidatorRegistry, jt as Patch, r as ApplyResult } from "./coreTypes-
|
|
2
|
+
import { At as ParsedForm, Nt as PatchSchema, U as FieldResponse, Y as FormSchema, at as InspectResult, et as Id, hr as ValidatorRegistry, jt as Patch, r as ApplyResult } from "./coreTypes-BlsJkU1w.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
5
|
//#region src/integrations/toolTypes.d.ts
|
package/dist/ai-sdk.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
import { R as PatchSchema } from "./coreTypes-CTLr-NGd.mjs";
|
|
3
|
-
import { d as serializeForm, i as inspect, t as applyPatches } from "./apply-
|
|
3
|
+
import { d as serializeForm, i as inspect, t as applyPatches } from "./apply-KzQztrDV.mjs";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
//#region src/integrations/vercelAiSdkTools.ts
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
|
|
2
|
+
import { n as formatUrlAsMarkdownLink } from "./urlFormat-lls7CsEP.mjs";
|
|
2
3
|
import YAML from "yaml";
|
|
3
4
|
|
|
4
5
|
//#region src/errors.ts
|
|
5
|
-
const VERSION = "0.1.
|
|
6
|
+
const VERSION = "0.1.23";
|
|
6
7
|
/**
|
|
7
8
|
* Base error class for all markform errors.
|
|
8
9
|
* Consumers can catch this to handle any markform error.
|
|
@@ -94,12 +95,15 @@ var MarkformLlmError = class extends MarkformError {
|
|
|
94
95
|
statusCode;
|
|
95
96
|
/** Whether this error is retryable */
|
|
96
97
|
retryable;
|
|
98
|
+
/** Raw response body from the API (for debugging) */
|
|
99
|
+
responseBody;
|
|
97
100
|
constructor(message, context) {
|
|
98
101
|
super(message, { cause: context.cause });
|
|
99
102
|
this.provider = context.provider;
|
|
100
103
|
this.model = context.model;
|
|
101
104
|
this.statusCode = context.statusCode;
|
|
102
105
|
this.retryable = context.retryable ?? false;
|
|
106
|
+
this.responseBody = context.responseBody;
|
|
103
107
|
}
|
|
104
108
|
};
|
|
105
109
|
/**
|
|
@@ -173,6 +177,86 @@ function isRetryableError(error) {
|
|
|
173
177
|
return isLlmError(error) && error.retryable;
|
|
174
178
|
}
|
|
175
179
|
/**
|
|
180
|
+
* Check if an error looks like a Vercel AI SDK APICallError.
|
|
181
|
+
* These errors have statusCode, responseBody, and isRetryable properties.
|
|
182
|
+
*/
|
|
183
|
+
function isApiCallError(error) {
|
|
184
|
+
return error instanceof Error && ("statusCode" in error || "responseBody" in error || "isRetryable" in error);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Generate troubleshooting hints based on error status and message.
|
|
188
|
+
*/
|
|
189
|
+
function getTroubleshootingHints(statusCode, message, provider, model) {
|
|
190
|
+
const hints = [];
|
|
191
|
+
const lowerMessage = message.toLowerCase();
|
|
192
|
+
if (statusCode === 404 || lowerMessage.includes("not found")) {
|
|
193
|
+
hints.push(`Check if model "${model}" exists and is available for the ${provider} API`);
|
|
194
|
+
hints.push("Verify the model ID is spelled correctly (check provider documentation)");
|
|
195
|
+
hints.push("Some models require specific API tier access or waitlist approval");
|
|
196
|
+
} else if (statusCode === 403 || lowerMessage.includes("forbidden")) {
|
|
197
|
+
hints.push(`Your API key may not have permission to use model "${model}"`);
|
|
198
|
+
hints.push("Check that your API key has the required access tier/plan");
|
|
199
|
+
hints.push("Some preview models require explicit opt-in via provider dashboard");
|
|
200
|
+
} else if (statusCode === 401 || lowerMessage.includes("unauthorized") || lowerMessage.includes("invalid api key")) {
|
|
201
|
+
hints.push("Verify your API key is correct and not expired");
|
|
202
|
+
hints.push(`Check that the correct env var is set (e.g., ${provider.toUpperCase()}_API_KEY)`);
|
|
203
|
+
} else if (statusCode === 429 || lowerMessage.includes("rate limit")) {
|
|
204
|
+
hints.push("You have hit the rate limit - wait a moment and retry");
|
|
205
|
+
hints.push("Consider using a model with higher rate limits or upgrading your plan");
|
|
206
|
+
} else if (statusCode === 500 || statusCode === 502 || statusCode === 503) {
|
|
207
|
+
hints.push("The API provider is experiencing issues - retry in a few minutes");
|
|
208
|
+
hints.push("Check the provider status page for any ongoing incidents");
|
|
209
|
+
}
|
|
210
|
+
if (hints.length === 0) {
|
|
211
|
+
hints.push("Check your API key and model availability");
|
|
212
|
+
hints.push(`Run "markform models" to see available models for ${provider}`);
|
|
213
|
+
}
|
|
214
|
+
return hints;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Wrap an API error with rich context for debugging.
|
|
218
|
+
*
|
|
219
|
+
* Extracts details from Vercel AI SDK APICallError and creates a MarkformLlmError
|
|
220
|
+
* with actionable information including model ID, status code, response body, and
|
|
221
|
+
* troubleshooting hints.
|
|
222
|
+
*
|
|
223
|
+
* @param error - The original error from the API call
|
|
224
|
+
* @param provider - The LLM provider name (e.g., 'anthropic', 'openai')
|
|
225
|
+
* @param model - The model identifier that was requested
|
|
226
|
+
* @returns A MarkformLlmError with rich context
|
|
227
|
+
*/
|
|
228
|
+
function wrapApiError(error, provider, model) {
|
|
229
|
+
let statusCode;
|
|
230
|
+
let responseBody;
|
|
231
|
+
let retryable = false;
|
|
232
|
+
let originalMessage = "Unknown error";
|
|
233
|
+
if (isApiCallError(error)) {
|
|
234
|
+
statusCode = error.statusCode;
|
|
235
|
+
responseBody = error.responseBody;
|
|
236
|
+
retryable = error.isRetryable ?? false;
|
|
237
|
+
originalMessage = error.message;
|
|
238
|
+
} else if (error instanceof Error) originalMessage = error.message;
|
|
239
|
+
else originalMessage = String(error);
|
|
240
|
+
const parts = [];
|
|
241
|
+
parts.push(`API call failed for model "${provider}/${model}"`);
|
|
242
|
+
if (statusCode !== void 0) parts.push(`HTTP ${statusCode}`);
|
|
243
|
+
parts.push(originalMessage);
|
|
244
|
+
if (responseBody) {
|
|
245
|
+
const truncated = responseBody.length > 200 ? responseBody.slice(0, 200) + "..." : responseBody;
|
|
246
|
+
parts.push(`Response: ${truncated}`);
|
|
247
|
+
}
|
|
248
|
+
const hints = getTroubleshootingHints(statusCode, originalMessage, provider, model);
|
|
249
|
+
if (hints.length > 0) parts.push(`\n\nTroubleshooting:\n - ${hints.join("\n - ")}`);
|
|
250
|
+
return new MarkformLlmError(parts.join(": "), {
|
|
251
|
+
provider,
|
|
252
|
+
model,
|
|
253
|
+
statusCode,
|
|
254
|
+
retryable,
|
|
255
|
+
responseBody,
|
|
256
|
+
cause: error instanceof Error ? error : void 0
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
176
260
|
* Alias for MarkformParseError.
|
|
177
261
|
* @deprecated Use MarkformParseError instead. ParseError will be removed in a future version.
|
|
178
262
|
*/
|
|
@@ -707,6 +791,7 @@ function extractTableContent(node) {
|
|
|
707
791
|
function extractTextFromNode(n) {
|
|
708
792
|
if (!n || typeof n !== "object") return "";
|
|
709
793
|
if (n.type === "text" && typeof n.attributes?.content === "string") return n.attributes.content;
|
|
794
|
+
if (n.type === "link" && typeof n.attributes?.href === "string") return `[${n.children?.map(extractTextFromNode).join("") ?? ""}](${n.attributes.href})`;
|
|
710
795
|
if (n.children && Array.isArray(n.children)) return n.children.map(extractTextFromNode).join("");
|
|
711
796
|
return "";
|
|
712
797
|
}
|
|
@@ -1357,74 +1442,6 @@ function priorityKeyComparator(priorityKeys) {
|
|
|
1357
1442
|
});
|
|
1358
1443
|
}
|
|
1359
1444
|
|
|
1360
|
-
//#endregion
|
|
1361
|
-
//#region src/utils/urlFormat.ts
|
|
1362
|
-
/**
|
|
1363
|
-
* Create a friendly abbreviated display name for a URL.
|
|
1364
|
-
* - Drops "www." prefix from domain
|
|
1365
|
-
* - Adds first portion of path (up to maxPathChars) if present
|
|
1366
|
-
* - Adds ellipsis (…) if path is truncated
|
|
1367
|
-
*
|
|
1368
|
-
* @param url - The URL to abbreviate
|
|
1369
|
-
* @param maxPathChars - Maximum characters to include from the path (default: 12)
|
|
1370
|
-
* @returns Friendly abbreviated URL (e.g., "example.com/docs/api…")
|
|
1371
|
-
*/
|
|
1372
|
-
function friendlyUrlAbbrev(url, maxPathChars = 12) {
|
|
1373
|
-
try {
|
|
1374
|
-
const parsed = new URL(url);
|
|
1375
|
-
let hostname = parsed.hostname;
|
|
1376
|
-
if (hostname.startsWith("www.")) hostname = hostname.slice(4);
|
|
1377
|
-
const path = parsed.pathname.slice(1);
|
|
1378
|
-
if (!path) return hostname;
|
|
1379
|
-
if (path.length <= maxPathChars) return `${hostname}/${path}`;
|
|
1380
|
-
return `${hostname}/${path.slice(0, maxPathChars)}…`;
|
|
1381
|
-
} catch {
|
|
1382
|
-
let result = url;
|
|
1383
|
-
result = result.replace(/^https?:\/\//, "");
|
|
1384
|
-
result = result.replace(/^www\./, "");
|
|
1385
|
-
const maxLen = 30;
|
|
1386
|
-
if (result.length > maxLen) return result.slice(0, maxLen) + "…";
|
|
1387
|
-
return result;
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
/**
|
|
1391
|
-
* Format a URL as a markdown link with a friendly abbreviated display text.
|
|
1392
|
-
* The full URL is preserved as the link target.
|
|
1393
|
-
*
|
|
1394
|
-
* @param url - The URL to format
|
|
1395
|
-
* @returns Markdown link in format [friendly-abbrev](url)
|
|
1396
|
-
*/
|
|
1397
|
-
function formatUrlAsMarkdownLink(url) {
|
|
1398
|
-
return `[${friendlyUrlAbbrev(url)}](${url})`;
|
|
1399
|
-
}
|
|
1400
|
-
/**
|
|
1401
|
-
* Format bare URLs in text as HTML links with abbreviated display text.
|
|
1402
|
-
* Also handles markdown-style links [text](url) for consistency.
|
|
1403
|
-
*
|
|
1404
|
-
* Processing order:
|
|
1405
|
-
* 1. Escape all HTML to prevent XSS
|
|
1406
|
-
* 2. Convert markdown links [text](url) to <a> tags
|
|
1407
|
-
* 3. Convert bare URLs (not already in links) to <a> tags with abbreviated display
|
|
1408
|
-
*
|
|
1409
|
-
* @param text - The raw text containing URLs (will be HTML-escaped)
|
|
1410
|
-
* @param escapeHtml - Function to escape HTML entities
|
|
1411
|
-
* @returns HTML-safe text with URLs converted to <a> tags
|
|
1412
|
-
*/
|
|
1413
|
-
function formatBareUrlsAsHtmlLinks(text, escapeHtml) {
|
|
1414
|
-
let result = escapeHtml(text);
|
|
1415
|
-
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, linkText, url) => {
|
|
1416
|
-
const cleanUrl = url.replace(/&/g, "&");
|
|
1417
|
-
return `<a href="${escapeHtml(cleanUrl)}" target="_blank" class="url-link" data-url="${escapeHtml(cleanUrl)}">${linkText}</a>`;
|
|
1418
|
-
});
|
|
1419
|
-
result = result.replace(/(?<!href="|data-url="|">)(?:https?:\/\/|www\.)[^\s<>"]+(?<![.,;:!?'")])/g, (url) => {
|
|
1420
|
-
const cleanUrl = url.replace(/&/g, "&");
|
|
1421
|
-
const fullUrl = cleanUrl.startsWith("www.") ? `https://${cleanUrl}` : cleanUrl;
|
|
1422
|
-
const display = friendlyUrlAbbrev(fullUrl);
|
|
1423
|
-
return `<a href="${escapeHtml(fullUrl)}" target="_blank" class="url-link" data-url="${escapeHtml(fullUrl)}">${escapeHtml(display)}</a>`;
|
|
1424
|
-
});
|
|
1425
|
-
return result;
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
1445
|
//#endregion
|
|
1429
1446
|
//#region src/engine/serialize.ts
|
|
1430
1447
|
/**
|
|
@@ -1976,14 +1993,13 @@ function serializeYearField(field, response) {
|
|
|
1976
1993
|
}
|
|
1977
1994
|
/**
|
|
1978
1995
|
* Serialize a cell value for table output.
|
|
1979
|
-
*
|
|
1996
|
+
* Values are serialized as-is; HTML rendering handles display formatting.
|
|
1980
1997
|
*/
|
|
1981
|
-
function serializeCellValue(cell,
|
|
1998
|
+
function serializeCellValue(cell, _columnType) {
|
|
1982
1999
|
if (cell.state === "skipped") return cell.reason ? `%SKIP:${cell.reason}%` : "%SKIP%";
|
|
1983
2000
|
if (cell.state === "aborted") return cell.reason ? `%ABORT:${cell.reason}%` : "%ABORT%";
|
|
1984
2001
|
if (cell.value === void 0 || cell.value === null) return "";
|
|
1985
2002
|
if (typeof cell.value === "number") return String(cell.value);
|
|
1986
|
-
if (columnType === "url") return formatUrlAsMarkdownLink(cell.value);
|
|
1987
2003
|
return cell.value;
|
|
1988
2004
|
}
|
|
1989
2005
|
/**
|
|
@@ -2383,6 +2399,16 @@ function serializeFieldRaw(field, responses) {
|
|
|
2383
2399
|
const lines = [];
|
|
2384
2400
|
lines.push(`**${field.label}:**`);
|
|
2385
2401
|
lines.push("");
|
|
2402
|
+
if (response?.state === "skipped") {
|
|
2403
|
+
const text = response.reason ? `_(skipped: ${response.reason})_` : "_(skipped)_";
|
|
2404
|
+
lines.push(text);
|
|
2405
|
+
return lines.join("\n");
|
|
2406
|
+
}
|
|
2407
|
+
if (response?.state === "aborted") {
|
|
2408
|
+
const text = response.reason ? `_(aborted: ${response.reason})_` : "_(aborted)_";
|
|
2409
|
+
lines.push(text);
|
|
2410
|
+
return lines.join("\n");
|
|
2411
|
+
}
|
|
2386
2412
|
const value = response?.state === "answered" ? response.value : void 0;
|
|
2387
2413
|
switch (field.kind) {
|
|
2388
2414
|
case "string": {
|
|
@@ -4330,5 +4356,5 @@ function applyPatches(form, patches) {
|
|
|
4330
4356
|
}
|
|
4331
4357
|
|
|
4332
4358
|
//#endregion
|
|
4333
|
-
export {
|
|
4334
|
-
//# sourceMappingURL=apply-
|
|
4359
|
+
export { WEB_SEARCH_CONFIG as $, DEFAULT_FORMS_DIR as A, DEFAULT_ROLES as B, getNumberAttr as C, isTagNode as D, getValidateAttr as E, DEFAULT_MAX_TURNS as F, deriveExportPath as G, MAX_FORMS_IN_MENU as H, DEFAULT_PORT as I, deriveSchemaPath as J, deriveFillRecordPath as K, DEFAULT_PRIORITY as L, DEFAULT_MAX_PARALLEL_AGENTS as M, DEFAULT_MAX_PATCHES_PER_TURN as N, parseOptionText as O, DEFAULT_MAX_STEPS_PER_TURN as P, SUGGESTED_LLMS as Q, DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN as R, getBooleanAttr as S, getStringAttr as T, REPORT_EXTENSION as U, DEFAULT_ROLE_INSTRUCTIONS as V, USER_ROLE as W, parseRolesFlag as X, detectFileType as Y, transformHarnessConfigToTs as Z, tryParseSentinelResponse as _, isPatchError as _t, validate as a, MarkformConfigError as at, extractOptionItems as b, wrapApiError as bt, computeProgressSummary as c, MarkformParseError as ct, serializeForm as d, ParseError as dt, formatSuggestedLlms as et, serializeRawMarkdown as f, isAbortError as ft, validateSyntaxConsistency as g, isParseError as gt, preprocessCommentSyntax as h, isMarkformError as ht, inspect as i, MarkformAbortError as it, DEFAULT_MAX_ISSUES_PER_TURN as j, AGENT_ROLE as k, computeStructureSummary as l, MarkformPatchError as lt, detectSyntaxStyle as m, isLlmError as mt, getAllFields as n, hasWebSearchSupport as nt, computeAllSummaries as o, MarkformError as ot, serializeReport as p, isConfigError as pt, deriveReportPath as q, getFieldsForRoles as r, parseModelIdForDisplay as rt, computeFormState as s, MarkformLlmError as st, applyPatches as t, getWebSearchConfig as tt, isFormComplete as u, MarkformValidationError as ut, CHECKBOX_MARKERS as v, isRetryableError as vt, getStringArrayAttr as w, extractTableContent as x, extractFenceValue as y, isValidationError as yt, DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN as z };
|
|
4360
|
+
//# sourceMappingURL=apply-KzQztrDV.mjs.map
|