tickmarkr 1.77.0 → 1.78.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/drivers/herdr.d.ts +7 -1
- package/dist/drivers/herdr.js +33 -7
- package/package.json +1 -1
package/dist/drivers/herdr.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { type ExecutorDriver, type NotifyOpts, type Slot, type SlotOpts } from "
|
|
|
2
2
|
export declare const TRAILER_SAFE_FLOOR_COLS = 108;
|
|
3
3
|
export declare const TRAILER_WIDTH_MARGIN = 2;
|
|
4
4
|
export declare const DELIVERY_ATTEMPTS = 3;
|
|
5
|
+
export interface HerdrTimeSource {
|
|
6
|
+
now: () => number;
|
|
7
|
+
sleep: (ms: number) => Promise<void>;
|
|
8
|
+
}
|
|
5
9
|
export declare class DeliveryReadinessError extends Error {
|
|
6
10
|
readonly waitedMs: number;
|
|
7
11
|
readonly transcript: string;
|
|
@@ -13,6 +17,7 @@ export declare function workerSplitDirection(paneCols: number | null, safeFloor?
|
|
|
13
17
|
export declare class HerdrDriver implements ExecutorDriver {
|
|
14
18
|
private bin;
|
|
15
19
|
private workersPerTab;
|
|
20
|
+
private time;
|
|
16
21
|
id: string;
|
|
17
22
|
interactive: boolean;
|
|
18
23
|
private groups;
|
|
@@ -24,12 +29,13 @@ export declare class HerdrDriver implements ExecutorDriver {
|
|
|
24
29
|
private ws;
|
|
25
30
|
private callerPane;
|
|
26
31
|
private watches;
|
|
27
|
-
constructor(bin?: string, workersPerTab?: number);
|
|
32
|
+
constructor(bin?: string, workersPerTab?: number, time?: HerdrTimeSource);
|
|
28
33
|
private serial;
|
|
29
34
|
private deliveryQueue;
|
|
30
35
|
private reserveDispatch;
|
|
31
36
|
private verifyPaneIdentityBinding;
|
|
32
37
|
private deliveryMatches;
|
|
38
|
+
private shellExecutionEchoed;
|
|
33
39
|
private submissionRegistered;
|
|
34
40
|
static available(): boolean;
|
|
35
41
|
private herdr;
|
package/dist/drivers/herdr.js
CHANGED
|
@@ -14,6 +14,10 @@ const DELIVERY_READ_LINES = 80;
|
|
|
14
14
|
const DELIVERY_SETTLE_READ_ATTEMPTS = 6;
|
|
15
15
|
const DELIVERY_SETTLE_POLL_MS = 100;
|
|
16
16
|
const DELIVERY_READINESS_TIMEOUT_MS = 1_000;
|
|
17
|
+
const SYSTEM_TIME = {
|
|
18
|
+
now: () => Date.now(),
|
|
19
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
20
|
+
};
|
|
17
21
|
// OBS-142: a typed identity lets the daemon's next task distinguish cold-start variance from
|
|
18
22
|
// structural driver faults without parsing prose. The message also carries the bounded wait and
|
|
19
23
|
// final pane evidence so a terminal failure is diagnosable on its own.
|
|
@@ -37,6 +41,7 @@ export function workerSplitDirection(paneCols, safeFloor = TRAILER_SAFE_FLOOR_CO
|
|
|
37
41
|
export class HerdrDriver {
|
|
38
42
|
bin;
|
|
39
43
|
workersPerTab;
|
|
44
|
+
time;
|
|
40
45
|
id = "herdr";
|
|
41
46
|
interactive = true;
|
|
42
47
|
groups = new Map();
|
|
@@ -55,9 +60,10 @@ export class HerdrDriver {
|
|
|
55
60
|
ws = process.env.HERDR_WORKSPACE_ID;
|
|
56
61
|
callerPane = process.env.HERDR_PANE_ID;
|
|
57
62
|
watches = new Map();
|
|
58
|
-
constructor(bin = "herdr", workersPerTab = 3) {
|
|
63
|
+
constructor(bin = "herdr", workersPerTab = 3, time = SYSTEM_TIME) {
|
|
59
64
|
this.bin = bin;
|
|
60
65
|
this.workersPerTab = workersPerTab;
|
|
66
|
+
this.time = time;
|
|
61
67
|
}
|
|
62
68
|
serial(fn) {
|
|
63
69
|
const p = this.groupSerial.then(fn, fn);
|
|
@@ -111,9 +117,27 @@ export class HerdrDriver {
|
|
|
111
117
|
const needle = norm(cmd);
|
|
112
118
|
return needle.length > 0 && hay.includes(needle);
|
|
113
119
|
}
|
|
120
|
+
shellExecutionEchoed(transcript, cmd) {
|
|
121
|
+
const norm = (s) => s.replace(/\u001B\[[0-9;?]*[ -/]*[@-~]/g, "").replace(/\s+/g, " ").trim();
|
|
122
|
+
const needle = norm(cmd);
|
|
123
|
+
if (!needle)
|
|
124
|
+
return false;
|
|
125
|
+
return transcript.split("\n").some((rawLine) => {
|
|
126
|
+
const line = norm(rawLine);
|
|
127
|
+
if (line === needle || !line.endsWith(needle))
|
|
128
|
+
return false;
|
|
129
|
+
const prefix = line.slice(0, -needle.length).trimEnd();
|
|
130
|
+
if (/[│┃╭╰┌└]/.test(prefix))
|
|
131
|
+
return false;
|
|
132
|
+
return /^(?:[$%#>]|[➜❯❱›»λ])(?:\s|$)/.test(prefix)
|
|
133
|
+
|| /[$%#>✗❯❱›»λ]$/.test(prefix);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
114
136
|
// Submission requires positive post-Enter evidence. A prompt echoed above a fresh input target
|
|
115
137
|
// proves registration; a declared box that is visibly empty after the verified paste also proves
|
|
116
|
-
// it consumed the prompt.
|
|
138
|
+
// it consumed the prompt. At the launch stage, a structurally prompt-prefixed shell execution echo
|
|
139
|
+
// is stronger evidence than waiting for first paint from the launched interface (OBS-144).
|
|
140
|
+
// Prompt absence alone is never success (OBS-142).
|
|
117
141
|
submissionRegistered(transcript, cmd, inputBox) {
|
|
118
142
|
const norm = (s) => s.replace(/\s+/g, "");
|
|
119
143
|
const hay = norm(transcript);
|
|
@@ -121,6 +145,8 @@ export class HerdrDriver {
|
|
|
121
145
|
const promptAt = hay.lastIndexOf(needle);
|
|
122
146
|
if (needle.length === 0)
|
|
123
147
|
return false;
|
|
148
|
+
if (inputBox?.launchCommand?.(cmd) === true && this.shellExecutionEchoed(transcript, cmd))
|
|
149
|
+
return true;
|
|
124
150
|
if (promptAt < 0)
|
|
125
151
|
return inputBox !== undefined && matchesEmptyInputBox(transcript, inputBox);
|
|
126
152
|
if (inputBox && matchesInputBox(transcript, inputBox)) {
|
|
@@ -527,7 +553,7 @@ export class HerdrDriver {
|
|
|
527
553
|
}
|
|
528
554
|
transcript = read.stdout;
|
|
529
555
|
if (readAttempt < readAttempts - 1) {
|
|
530
|
-
await
|
|
556
|
+
await this.time.sleep(DELIVERY_SETTLE_POLL_MS);
|
|
531
557
|
}
|
|
532
558
|
}
|
|
533
559
|
return { ok: false, transcript, recognizedInputBox: false };
|
|
@@ -536,19 +562,19 @@ export class HerdrDriver {
|
|
|
536
562
|
const inputBox = this.inputBoxes.get(slot);
|
|
537
563
|
const requireInputBox = inputBox !== undefined && inputBox.launchCommand?.(cmd) !== true;
|
|
538
564
|
const timeoutMs = inputBox?.readinessTimeoutMs ?? DELIVERY_READINESS_TIMEOUT_MS;
|
|
539
|
-
const started =
|
|
565
|
+
const started = this.time.now();
|
|
540
566
|
let previous;
|
|
541
567
|
let transcript = "";
|
|
542
568
|
let reads = 0;
|
|
543
569
|
while (true) {
|
|
544
|
-
const elapsedBeforeRead =
|
|
570
|
+
const elapsedBeforeRead = this.time.now() - started;
|
|
545
571
|
const remainingBeforeRead = timeoutMs - elapsedBeforeRead;
|
|
546
572
|
if (remainingBeforeRead <= 0)
|
|
547
573
|
throw new DeliveryReadinessError(elapsedBeforeRead, transcript);
|
|
548
574
|
const read = await this.herdr(`pane read ${shq(pane)} --source recent-unwrapped --lines ${DELIVERY_READ_LINES}`, slot.cwd, Math.max(1, Math.ceil(remainingBeforeRead)));
|
|
549
575
|
reads++;
|
|
550
576
|
transcript = read.stdout || transcript;
|
|
551
|
-
const waitedMs =
|
|
577
|
+
const waitedMs = this.time.now() - started;
|
|
552
578
|
if (read.code !== 0) {
|
|
553
579
|
// Once at least one valid frame has been observed, a read that consumes the remainder of
|
|
554
580
|
// the readiness budget is the bounded window expiring, not a new submission/protocol
|
|
@@ -574,7 +600,7 @@ export class HerdrDriver {
|
|
|
574
600
|
// The second read is immediate: an already-painted stable target proves readiness with no
|
|
575
601
|
// added delay. Only observed change spends a poll interval, and every path remains bounded.
|
|
576
602
|
if (reads > 1) {
|
|
577
|
-
await
|
|
603
|
+
await this.time.sleep(Math.min(DELIVERY_SETTLE_POLL_MS, remaining));
|
|
578
604
|
}
|
|
579
605
|
}
|
|
580
606
|
}
|