harnery 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commander.d.ts +10 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +2 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +43 -29
- package/dist/commands/browse-ai.js +1 -1
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +41 -9
- package/dist/commands/cookies.js +1 -1
- package/dist/commands/decision.d.ts +4 -0
- package/dist/commands/decision.d.ts.map +1 -0
- package/dist/commands/decision.js +354 -0
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +5 -1
- package/dist/commands/fetch.js +1 -1
- package/dist/core/agents/events/consume.d.ts +25 -2
- package/dist/core/agents/events/consume.d.ts.map +1 -1
- package/dist/core/agents/events/consume.js +55 -7
- package/dist/core/agents/events/emit.d.ts +2 -1
- package/dist/core/agents/events/emit.d.ts.map +1 -1
- package/dist/core/agents/events/emit.js +2 -1
- package/dist/core/agents/state/scratch.d.ts +1 -1
- package/dist/core/agents/state/scratch.js +2 -2
- package/dist/core/hooks/effects/index.d.ts.map +1 -1
- package/dist/core/hooks/effects/index.js +2 -1
- package/dist/lib/agent-browser/client.js +1 -1
- package/dist/lib/browser/client.d.ts +14 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +20 -0
- package/dist/lib/browser/index.d.ts +1 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/runts.d.ts +44 -0
- package/dist/lib/browser/runts.d.ts.map +1 -0
- package/dist/lib/browser/runts.js +193 -0
- package/dist/lib/completion/walk.js +1 -1
- package/dist/lib/cookies/client.d.ts +1 -1
- package/dist/lib/cookies/client.d.ts.map +1 -1
- package/dist/lib/cookies/client.js +1 -1
- package/dist/lib/decision/index.d.ts +212 -0
- package/dist/lib/decision/index.d.ts.map +1 -0
- package/dist/lib/decision/index.js +523 -0
- package/dist/lib/docs-lint.d.ts +1 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +49 -0
- package/dist/lib/tunnel/gate.js +1 -1
- package/package.json +3 -1
- package/src/commander.ts +12 -0
- package/src/commands/agents.ts +47 -26
- package/src/commands/browse-ai.ts +1 -1
- package/src/commands/browse.ts +63 -8
- package/src/commands/cookies.ts +1 -1
- package/src/commands/decision.ts +438 -0
- package/src/commands/docs.ts +5 -1
- package/src/commands/fetch.ts +1 -1
- package/src/core/agents/events/consume.ts +65 -7
- package/src/core/agents/events/emit.ts +2 -1
- package/src/core/agents/state/scratch.ts +2 -2
- package/src/core/config.ts +1 -1
- package/src/core/hooks/effects/index.ts +2 -1
- package/src/lib/agent-browser/client.ts +1 -1
- package/src/lib/browser/client.ts +28 -0
- package/src/lib/browser/index.ts +4 -0
- package/src/lib/browser/runts.ts +218 -0
- package/src/lib/completion/walk.ts +1 -1
- package/src/lib/cookies/client.ts +2 -2
- package/src/lib/decision/index.ts +685 -0
- package/src/lib/docs-lint.ts +44 -0
- package/src/lib/tunnel/gate.ts +1 -1
package/src/commands/agents.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { homedir, tmpdir } from "node:os";
|
|
|
28
28
|
import { basename, join, resolve } from "node:path";
|
|
29
29
|
import type { Command } from "commander";
|
|
30
30
|
import type { EmitContext } from "../commander.ts";
|
|
31
|
+
import { readStreamTailBounded } from "../core/agents/events/consume.ts";
|
|
31
32
|
import {
|
|
32
33
|
emitCanonical,
|
|
33
34
|
type Heartbeat,
|
|
@@ -38,6 +39,12 @@ import {
|
|
|
38
39
|
resolveOwnerWithSource,
|
|
39
40
|
} from "../core/agents/index.ts";
|
|
40
41
|
import { resolveBinName } from "../core/config.ts";
|
|
42
|
+
|
|
43
|
+
/** Cap for CLI scans of the unbounded event ledger (`trace` / `health`). Well
|
|
44
|
+
* under V8's ~512MB max string length so a `readFileSync` of the whole file can
|
|
45
|
+
* never throw; covers ample recent history for a diagnostic scan. */
|
|
46
|
+
const STREAM_SCAN_CAP_BYTES = 128 * 1024 * 1024; // 128 MiB
|
|
47
|
+
|
|
41
48
|
import { parsePsChainLine } from "../core/hooks/resolve/anchor.ts";
|
|
42
49
|
import {
|
|
43
50
|
buildCouncilId,
|
|
@@ -2098,8 +2105,12 @@ function readStreamStats(root: string): { bytes: number; lines: number; cursor_b
|
|
|
2098
2105
|
let backlog = 0;
|
|
2099
2106
|
let seenCursor = cursor === null; // no cursor → everything is "backlog"
|
|
2100
2107
|
try {
|
|
2101
|
-
|
|
2102
|
-
|
|
2108
|
+
// Bounded tail read: the ledger grows without bound and a whole-file
|
|
2109
|
+
// readFileSync throws past V8's ~512MB string limit. lines/backlog are then
|
|
2110
|
+
// scoped to the scanned window; `bytes` (full size, via statSync above) still
|
|
2111
|
+
// reflects the true stream size for the health rollup.
|
|
2112
|
+
const { text } = readStreamTailBounded(streamPath, STREAM_SCAN_CAP_BYTES);
|
|
2113
|
+
for (const line of text.split("\n")) {
|
|
2103
2114
|
if (!line.trim()) continue;
|
|
2104
2115
|
lines++;
|
|
2105
2116
|
if (seenCursor) {
|
|
@@ -2225,24 +2236,34 @@ function runTrace(
|
|
|
2225
2236
|
const limit = Math.max(1, Number.parseInt(opts.limit, 10) || 200);
|
|
2226
2237
|
const candidateIds = targetId.includes("\x00") ? targetId.split("\x00") : [targetId];
|
|
2227
2238
|
|
|
2228
|
-
// Scan the
|
|
2239
|
+
// Scan the stream tail once, bucket events by instance_id for the candidates.
|
|
2240
|
+
// Bounded read: the ledger grows without bound and a whole-file readFileSync
|
|
2241
|
+
// throws past V8's ~512MB string limit. A trace of an agent whose events all
|
|
2242
|
+
// predate the window is reported as truncated rather than crashing.
|
|
2229
2243
|
const streamPath = resolve(root, ".harnery", "events.ndjson");
|
|
2230
2244
|
const byId = new Map<string, CanonicalEvent[]>();
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2245
|
+
const { text: streamText, truncated: streamTruncated } = readStreamTailBounded(
|
|
2246
|
+
streamPath,
|
|
2247
|
+
STREAM_SCAN_CAP_BYTES,
|
|
2248
|
+
);
|
|
2249
|
+
for (const line of streamText.split("\n")) {
|
|
2250
|
+
if (!line) continue;
|
|
2251
|
+
try {
|
|
2252
|
+
const ev = JSON.parse(line) as CanonicalEvent;
|
|
2253
|
+
if (!ev.instance_id || !candidateIds.includes(ev.instance_id)) continue;
|
|
2254
|
+
if (sinceMs && Date.parse(ev.ts) < sinceMs) continue;
|
|
2255
|
+
const arr = byId.get(ev.instance_id) ?? [];
|
|
2256
|
+
arr.push(ev);
|
|
2257
|
+
byId.set(ev.instance_id, arr);
|
|
2258
|
+
} catch {
|
|
2259
|
+
/* skip */
|
|
2244
2260
|
}
|
|
2245
2261
|
}
|
|
2262
|
+
if (streamTruncated) {
|
|
2263
|
+
process.stderr.write(
|
|
2264
|
+
`note: event ledger exceeds ${Math.round(STREAM_SCAN_CAP_BYTES / 1024 / 1024)}MB; traced only the most recent window (older events omitted)\n`,
|
|
2265
|
+
);
|
|
2266
|
+
}
|
|
2246
2267
|
|
|
2247
2268
|
// If the name mapped to multiple instances, trace the one with the latest event.
|
|
2248
2269
|
let resolvedId = candidateIds[0]!;
|
|
@@ -2629,7 +2650,7 @@ function renderHealthBox(report: HealthReport): void {
|
|
|
2629
2650
|
const localTime = formatLocalTime(new Date(report.generated_at));
|
|
2630
2651
|
const title = `Coord Health (${localTime})`;
|
|
2631
2652
|
|
|
2632
|
-
process.stdout.write(`${formatBox(title, rows)}\n`); // lint-ok-emission: chat-paste path; mirrors runStatus's direct write so the box surfaces in both TTY +
|
|
2653
|
+
process.stdout.write(`${formatBox(title, rows)}\n`); // lint-ok-emission: chat-paste path; mirrors runStatus's direct write so the box surfaces in both TTY + harn-session-teed contexts
|
|
2633
2654
|
|
|
2634
2655
|
if (report.anomalies.length > 0) {
|
|
2635
2656
|
process.stdout.write("\n"); // lint-ok-emission: same chat-paste path
|
|
@@ -2686,7 +2707,7 @@ function runHarnessProbe(
|
|
|
2686
2707
|
// and `resolveOwner` here, so the probe reports exactly what the live hot
|
|
2687
2708
|
// path resolves.
|
|
2688
2709
|
const anchorTokens = new Set(["claude", "claude-code", "cursor", "codex"]);
|
|
2689
|
-
const override = process.env.
|
|
2710
|
+
const override = process.env.HARNERY_AGENT_COORD_TEST_ANCHOR_PID;
|
|
2690
2711
|
let anchorPid = override && Number(override) > 0 ? override : "";
|
|
2691
2712
|
const chainParts: string[] = [];
|
|
2692
2713
|
let walkPid = process.pid;
|
|
@@ -2795,12 +2816,12 @@ function runHarnessProbe(
|
|
|
2795
2816
|
* dispatcher in an isolated sandbox.
|
|
2796
2817
|
*
|
|
2797
2818
|
* Sandbox isolation strategy:
|
|
2798
|
-
* - mkdtempSync(tmpdir(), "
|
|
2819
|
+
* - mkdtempSync(tmpdir(), "harn-harness-probe-") creates a non-git tmp dir.
|
|
2799
2820
|
* - The dispatcher's coord-root resolution falls back to
|
|
2800
|
-
* `
|
|
2821
|
+
* `HARNERY_COORD_ROOT_OVERRIDE` when git rev-parse fails. We set it to the sandbox.
|
|
2801
2822
|
* - We rewrite the payload's `cwd` field (Cursor cds to it) to the sandbox,
|
|
2802
2823
|
* so real `.harnery/` never gets touched.
|
|
2803
|
-
* - We set `
|
|
2824
|
+
* - We set `HARNERY_AGENT_COORD_OFF=0` explicitly so any user-side off-switch in
|
|
2804
2825
|
* the environment doesn't mask adapter crashes.
|
|
2805
2826
|
*
|
|
2806
2827
|
* Sample shape: probe-meta wrapped (`_probe_meta.event` + `.payload`) OR bare
|
|
@@ -2880,7 +2901,7 @@ function replayHarnessSamples(
|
|
|
2880
2901
|
};
|
|
2881
2902
|
const harnessFlag = harness === "claude_code" ? "claude-code" : harness;
|
|
2882
2903
|
|
|
2883
|
-
const sandbox = mkdtempSync(join(tmpdir(), "
|
|
2904
|
+
const sandbox = mkdtempSync(join(tmpdir(), "harn-harness-probe-"));
|
|
2884
2905
|
const results: SampleReplayResult[] = [];
|
|
2885
2906
|
|
|
2886
2907
|
try {
|
|
@@ -2928,10 +2949,10 @@ function replayHarnessSamples(
|
|
|
2928
2949
|
timeout: 10_000,
|
|
2929
2950
|
env: {
|
|
2930
2951
|
...process.env,
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2952
|
+
HARNERY_COORD_ROOT_OVERRIDE: sandbox,
|
|
2953
|
+
HARNERY_AGENT_COORD_HARNESS: harness,
|
|
2954
|
+
HARNERY_AGENT_COORD_PLATFORM: harness,
|
|
2955
|
+
HARNERY_AGENT_COORD_OFF: "0",
|
|
2935
2956
|
},
|
|
2936
2957
|
});
|
|
2937
2958
|
|
|
@@ -99,7 +99,7 @@ function runBrowseAi(url: string, opts: BrowseAiOpts): void {
|
|
|
99
99
|
const jar =
|
|
100
100
|
opts.cookies === false
|
|
101
101
|
? null
|
|
102
|
-
: new CookieJar({ path: opts.store ?? DEFAULT_STORE, source: "
|
|
102
|
+
: new CookieJar({ path: opts.store ?? DEFAULT_STORE, source: "harn-browse-ai" });
|
|
103
103
|
const ab = new AgentBrowser({
|
|
104
104
|
jar,
|
|
105
105
|
timeoutMs: Number.parseInt(opts.timeout, 10),
|
package/src/commands/browse.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type DevOverlayResult,
|
|
11
11
|
type Diagnostics,
|
|
12
12
|
type OverflowResult,
|
|
13
|
+
type RuntsResult,
|
|
13
14
|
type VisibilityResult,
|
|
14
15
|
type WidthResult,
|
|
15
16
|
} from "../lib/browser/index.ts";
|
|
@@ -77,16 +78,22 @@ interface BrowseOpts {
|
|
|
77
78
|
checkVisibleThreshold?: string;
|
|
78
79
|
checkVisibleFail?: boolean;
|
|
79
80
|
checkVisibleSampleGrid?: string;
|
|
80
|
-
|
|
81
|
+
// Commander maps `--no-check-visible-annotate` to `checkVisibleAnnotate: false`.
|
|
82
|
+
checkVisibleAnnotate?: boolean;
|
|
81
83
|
// Width-fill check
|
|
82
84
|
checkWidth?: string[];
|
|
83
85
|
checkWidthThreshold?: string;
|
|
84
86
|
checkWidthFail?: boolean;
|
|
85
|
-
|
|
87
|
+
checkWidthAnnotate?: boolean;
|
|
88
|
+
// `--check-runts [selector]`: true = whole body, string = scope selector.
|
|
89
|
+
checkRunts?: boolean | string;
|
|
90
|
+
checkRuntsMinChars?: string;
|
|
91
|
+
checkRuntsFail?: boolean;
|
|
92
|
+
checkRuntsAnnotate?: boolean;
|
|
86
93
|
// Horizontal-overflow check
|
|
87
94
|
checkOverflow?: boolean;
|
|
88
95
|
checkOverflowFail?: boolean;
|
|
89
|
-
|
|
96
|
+
checkOverflowAnnotate?: boolean;
|
|
90
97
|
// Visual regression
|
|
91
98
|
baseline?: string;
|
|
92
99
|
diff?: string;
|
|
@@ -232,6 +239,24 @@ export function registerBrowseCommand(
|
|
|
232
239
|
"--no-check-overflow-annotate",
|
|
233
240
|
"Skip drawing overflow annotations on the screenshot (JSON still emitted).",
|
|
234
241
|
)
|
|
242
|
+
.option(
|
|
243
|
+
"--check-runts [selector]",
|
|
244
|
+
"Scan text blocks for runts (a single word alone on a block's last visual line) " +
|
|
245
|
+
"by counting words on the last line via per-word Range rects — width thresholds " +
|
|
246
|
+
"miss narrow-column runts. Optional selector scopes the sweep (default: whole body). " +
|
|
247
|
+
"Atomic tokens (URLs, emails, phone numbers) are excluded. Reports hits in the JSON " +
|
|
248
|
+
"envelope under `runts` and annotates them on the screenshot.",
|
|
249
|
+
)
|
|
250
|
+
.option(
|
|
251
|
+
"--check-runts-min-chars <n>",
|
|
252
|
+
"Minimum block text length to scan (default 40; smaller labels can't meaningfully wrap).",
|
|
253
|
+
"40",
|
|
254
|
+
)
|
|
255
|
+
.option("--check-runts-fail", "Exit non-zero if any runt is detected.")
|
|
256
|
+
.option(
|
|
257
|
+
"--no-check-runts-annotate",
|
|
258
|
+
"Skip drawing runt boxes on the screenshot (JSON still emitted).",
|
|
259
|
+
)
|
|
235
260
|
.option(
|
|
236
261
|
"--baseline <name>",
|
|
237
262
|
"Save the captured screenshot as a named baseline at " +
|
|
@@ -275,7 +300,7 @@ async function runBrowse(
|
|
|
275
300
|
const jar =
|
|
276
301
|
opts.cookies === false
|
|
277
302
|
? null
|
|
278
|
-
: new CookieJar({ path: opts.store ?? DEFAULT_STORE, source: "
|
|
303
|
+
: new CookieJar({ path: opts.store ?? DEFAULT_STORE, source: "harn-browse" });
|
|
279
304
|
const headed = opts.login || opts.headed;
|
|
280
305
|
const viewport = parseViewport(opts.viewport ?? "desktop");
|
|
281
306
|
|
|
@@ -330,7 +355,7 @@ async function runBrowse(
|
|
|
330
355
|
visibility = await browser.checkVisibility(opts.checkVisible, {
|
|
331
356
|
sampleGrid: Number.parseInt(opts.checkVisibleSampleGrid ?? "3", 10),
|
|
332
357
|
});
|
|
333
|
-
if (opts.
|
|
358
|
+
if (opts.checkVisibleAnnotate !== false) {
|
|
334
359
|
await browser.annotateVisibility(visibility);
|
|
335
360
|
}
|
|
336
361
|
}
|
|
@@ -343,9 +368,16 @@ async function runBrowse(
|
|
|
343
368
|
if (opts.checkOverflow) {
|
|
344
369
|
overflow = await browser.checkOverflow();
|
|
345
370
|
}
|
|
371
|
+
let runts: RuntsResult | undefined;
|
|
372
|
+
if (opts.checkRunts) {
|
|
373
|
+
runts = await browser.checkRunts({
|
|
374
|
+
scope: typeof opts.checkRunts === "string" ? opts.checkRunts : null,
|
|
375
|
+
minChars: Number.parseInt(opts.checkRuntsMinChars ?? "40", 10),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
346
378
|
const widthThreshold = Number.parseFloat(opts.checkWidthThreshold ?? "0.9");
|
|
347
|
-
const annotateWidth = widths && opts.
|
|
348
|
-
const annotateOverflow = overflow && opts.
|
|
379
|
+
const annotateWidth = widths && opts.checkWidthAnnotate !== false;
|
|
380
|
+
const annotateOverflow = overflow && opts.checkOverflowAnnotate !== false;
|
|
349
381
|
if (annotateWidth || annotateOverflow) {
|
|
350
382
|
await browser.annotateLayout({
|
|
351
383
|
widths: annotateWidth ? widths! : [],
|
|
@@ -353,6 +385,10 @@ async function runBrowse(
|
|
|
353
385
|
widthThreshold,
|
|
354
386
|
});
|
|
355
387
|
}
|
|
388
|
+
const annotateRunts = runts && runts.runts.length > 0 && opts.checkRuntsAnnotate !== false;
|
|
389
|
+
if (annotateRunts) {
|
|
390
|
+
await browser.annotateRunts(runts!);
|
|
391
|
+
}
|
|
356
392
|
|
|
357
393
|
if (opts.login) {
|
|
358
394
|
await new Promise<void>((res) => {
|
|
@@ -385,6 +421,7 @@ async function runBrowse(
|
|
|
385
421
|
visibility,
|
|
386
422
|
widths,
|
|
387
423
|
overflow,
|
|
424
|
+
runts,
|
|
388
425
|
devOverlay,
|
|
389
426
|
batchResult,
|
|
390
427
|
);
|
|
@@ -397,17 +434,21 @@ async function runBrowse(
|
|
|
397
434
|
visibility,
|
|
398
435
|
widths,
|
|
399
436
|
overflow,
|
|
437
|
+
runts,
|
|
400
438
|
devOverlay,
|
|
401
439
|
batchResult,
|
|
402
440
|
);
|
|
403
441
|
}
|
|
404
442
|
|
|
405
|
-
if (visibility && opts.
|
|
443
|
+
if (visibility && opts.checkVisibleAnnotate !== false) {
|
|
406
444
|
await browser.clearVisibilityAnnotations();
|
|
407
445
|
}
|
|
408
446
|
if (annotateWidth || annotateOverflow) {
|
|
409
447
|
await browser.clearLayoutAnnotations();
|
|
410
448
|
}
|
|
449
|
+
if (annotateRunts) {
|
|
450
|
+
await browser.clearRuntsAnnotations();
|
|
451
|
+
}
|
|
411
452
|
|
|
412
453
|
if (opts.checkVisibleFail && visibility) {
|
|
413
454
|
const threshold = Number.parseFloat(opts.checkVisibleThreshold ?? "0.9");
|
|
@@ -446,6 +487,16 @@ async function runBrowse(
|
|
|
446
487
|
}
|
|
447
488
|
}
|
|
448
489
|
|
|
490
|
+
if (opts.checkRuntsFail && runts && runts.runts.length > 0) {
|
|
491
|
+
for (const hit of runts.runts) {
|
|
492
|
+
emit.log(
|
|
493
|
+
`check-runts FAIL ${hit.block}: last line is a lone "${hit.word}" ("…${hit.snippet.slice(-40)}")`,
|
|
494
|
+
"warn",
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
process.exitCode = 2;
|
|
498
|
+
}
|
|
499
|
+
|
|
449
500
|
if (opts.checkOverflowFail && overflow?.hasHorizontalOverflow) {
|
|
450
501
|
const culprit = overflow.widerThanViewport[0] ?? overflow.rightOverflow[0] ?? null;
|
|
451
502
|
const detail = culprit
|
|
@@ -578,6 +629,7 @@ async function runPrintMode(
|
|
|
578
629
|
visibility: VisibilityResult[] | undefined,
|
|
579
630
|
widths: WidthResult[] | undefined,
|
|
580
631
|
overflow: OverflowResult | undefined,
|
|
632
|
+
runts: RuntsResult | undefined,
|
|
581
633
|
devOverlay: DevOverlayResult | undefined,
|
|
582
634
|
batchResult: BatchResult | undefined,
|
|
583
635
|
): Promise<void> {
|
|
@@ -605,6 +657,7 @@ async function runPrintMode(
|
|
|
605
657
|
if (visibility) result.visibility = visibility;
|
|
606
658
|
if (widths) result.width = widths;
|
|
607
659
|
if (overflow) result.overflow = overflow;
|
|
660
|
+
if (runts) result.runts = runts;
|
|
608
661
|
if (devOverlay) result.devOverlay = devOverlay;
|
|
609
662
|
if (batchResult && batchResult.clipboardReads.length > 0) {
|
|
610
663
|
result.batchClipboardReads = batchResult.clipboardReads;
|
|
@@ -634,6 +687,7 @@ async function runTrioMode(
|
|
|
634
687
|
visibility: VisibilityResult[] | undefined,
|
|
635
688
|
widths: WidthResult[] | undefined,
|
|
636
689
|
overflow: OverflowResult | undefined,
|
|
690
|
+
runts: RuntsResult | undefined,
|
|
637
691
|
devOverlay: DevOverlayResult | undefined,
|
|
638
692
|
batchResult: BatchResult | undefined,
|
|
639
693
|
): Promise<void> {
|
|
@@ -675,6 +729,7 @@ async function runTrioMode(
|
|
|
675
729
|
if (visibility) envelope.visibility = visibility;
|
|
676
730
|
if (widths) envelope.width = widths;
|
|
677
731
|
if (overflow) envelope.overflow = overflow;
|
|
732
|
+
if (runts) envelope.runts = runts;
|
|
678
733
|
if (devOverlay) envelope.devOverlay = devOverlay;
|
|
679
734
|
if (batchResult && batchResult.clipboardReads.length > 0) {
|
|
680
735
|
envelope.batchClipboardReads = batchResult.clipboardReads;
|