tickmarkr 2.5.4 → 2.5.5
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/adapters/registry.js +6 -1
- package/dist/adapters/types.d.ts +3 -0
- package/dist/cli/commands/approve.d.ts +1 -0
- package/dist/cli/commands/approve.js +54 -6
- package/dist/cli/commands/doctor.js +44 -29
- package/dist/cli/commands/fleet.js +195 -33
- package/dist/cli/commands/init.js +196 -6
- package/dist/cli/commands/resume.js +4 -2
- package/dist/cli/commands/run.js +11 -2
- package/dist/cli/commands/verify.js +1 -1
- package/dist/cli/help.d.ts +2 -0
- package/dist/cli/help.js +3 -1
- package/dist/config/config.d.ts +14 -8
- package/dist/config/config.js +29 -26
- package/dist/config/fleet-overlay.d.ts +3 -9
- package/dist/config/fleet-overlay.js +68 -11
- package/dist/config/fleet-why.d.ts +7 -0
- package/dist/config/fleet-why.js +5 -0
- package/dist/drivers/index.d.ts +15 -1
- package/dist/drivers/index.js +38 -10
- package/dist/drivers/orca.d.ts +99 -10
- package/dist/drivers/orca.js +586 -97
- package/dist/gates/baseline.d.ts +3 -0
- package/dist/gates/baseline.js +2 -1
- package/dist/gates/cache.d.ts +100 -0
- package/dist/gates/cache.js +389 -0
- package/dist/gates/run-gates.d.ts +3 -0
- package/dist/gates/run-gates.js +117 -10
- package/dist/gates/test-manifest.d.ts +99 -0
- package/dist/gates/test-manifest.js +389 -0
- package/dist/gates/test-reporter.d.ts +4 -0
- package/dist/gates/test-reporter.js +49 -0
- package/dist/route/preference.d.ts +22 -1
- package/dist/route/preference.js +123 -25
- package/dist/route/router.js +31 -6
- package/dist/run/daemon.d.ts +13 -0
- package/dist/run/daemon.js +160 -70
- package/dist/run/git.d.ts +10 -1
- package/dist/run/git.js +43 -9
- package/dist/run/journal.d.ts +14 -2
- package/dist/run/journal.js +78 -10
- package/dist/run/lease.d.ts +14 -0
- package/dist/run/lease.js +87 -0
- package/dist/run/merge.d.ts +2 -0
- package/dist/run/merge.js +91 -3
- package/dist/run/operator-state.d.ts +11 -0
- package/dist/run/operator-state.js +17 -3
- package/dist/tui/cockpit/board.d.ts +96 -0
- package/dist/tui/cockpit/board.js +346 -0
- package/dist/tui/cockpit/decision-actions.js +2 -0
- package/dist/tui/cockpit/layout.d.ts +5 -1
- package/dist/tui/cockpit/layout.js +8 -3
- package/dist/tui/cockpit/live-runtime.js +71 -21
- package/dist/tui/cockpit/run-view.d.ts +7 -5
- package/dist/tui/cockpit/run-view.js +12 -11
- package/dist/tui/ink/fleet-app.d.ts +41 -27
- package/dist/tui/ink/fleet-app.js +204 -31
- package/package.json +1 -1
- package/skills/tickmarkr-overseer/SKILL.md +173 -113
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Fleet-overlay mutation, serialization, and diff rendering for the `tickmarkr fleet` write path.
|
|
2
2
|
import { isMap, isScalar, isSeq, parseDocument, stringify, visit } from "yaml";
|
|
3
|
-
import { universeCovers } from "./config.js";
|
|
3
|
+
import { universeCovers, universeEntryMatches } from "./config.js";
|
|
4
4
|
/** Fleet-owned overlay keys — the only config surface `tickmarkr fleet` may write. */
|
|
5
5
|
export const FLEET_OVERLAY_KEYS = ["routing", "tiers"];
|
|
6
6
|
function fleetSubset(obj) {
|
|
@@ -21,17 +21,18 @@ function allowFormFromExclusions(universe, edited) {
|
|
|
21
21
|
if (!universe.length) {
|
|
22
22
|
throw new Error("fleet write: universe is empty — no classified models to compute routing.allow from; classify models in `tickmarkr fleet` first");
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
// LEG2-T3 round 2 finding 1: every staged entry excludes what it NAMES — a bare model id every
|
|
25
|
+
// adapter serving it, an identity its alias — never only an adapter id or an adapter:model key.
|
|
26
|
+
const entries = [...edited.denyAdapters, ...edited.denyModels];
|
|
26
27
|
const adapters = [];
|
|
27
28
|
const models = [];
|
|
28
29
|
let excluded = false;
|
|
29
30
|
for (const row of universe) {
|
|
30
|
-
if (
|
|
31
|
+
if (entries.includes(row.adapter)) {
|
|
31
32
|
excluded = true;
|
|
32
33
|
continue;
|
|
33
34
|
}
|
|
34
|
-
const inFleet = row.models.filter((m) => !
|
|
35
|
+
const inFleet = row.models.filter((m) => !entries.some((entry) => universeEntryMatches(row, m, entry)));
|
|
35
36
|
if (inFleet.length === row.models.length) {
|
|
36
37
|
adapters.push(row.adapter);
|
|
37
38
|
}
|
|
@@ -49,6 +50,21 @@ function allowFormFromExclusions(universe, edited) {
|
|
|
49
50
|
function residualDeny(universe, entries) {
|
|
50
51
|
return sortedUnique(entries.filter((entry) => !universeCovers(universe, entry)));
|
|
51
52
|
}
|
|
53
|
+
// LEG2-T3 round 2 finding 2: the flat deny list a membership write leaves behind. Every entry the
|
|
54
|
+
// repo overlay authored in THIS list that is still staged stays verbatim, in its authored order and
|
|
55
|
+
// with its comments — one cleared reason never takes an independent one with it, and an untouched
|
|
56
|
+
// list keeps its node. A staged entry the allow form cannot express as a membership key (outside the
|
|
57
|
+
// probe universe, or a bare-model/identity spelling) is written verbatim too; canonical keys the
|
|
58
|
+
// session added ride the allow form alone.
|
|
59
|
+
function flatDenyAfterWrite(doc, path, after, universe) {
|
|
60
|
+
const node = doc.getIn(path, true);
|
|
61
|
+
const authored = isSeq(node) ? node.items.flatMap((item) => (isScalar(item) ? [String(item.value)] : [])) : [];
|
|
62
|
+
const canonical = (entry) => universe.some((row) => entry === row.adapter || row.models.some((m) => entry === `${row.adapter}:${m}`));
|
|
63
|
+
const kept = authored.filter((entry) => after.includes(entry));
|
|
64
|
+
const verbatim = sortedUnique(after.filter((entry) => !kept.includes(entry)
|
|
65
|
+
&& (!universeCovers(universe, entry) || !canonical(entry))));
|
|
66
|
+
return [...new Set([...kept, ...verbatim])];
|
|
67
|
+
}
|
|
52
68
|
// fleet.ts deliberately remains the sole overlay builder and writer. Its established classification
|
|
53
69
|
// seam copies only `tier` and `note` into FleetEditable, so first-touch entry metadata rides inside a
|
|
54
70
|
// private provenance envelope until this module writes the YAML. The envelope never reaches disk.
|
|
@@ -199,18 +215,35 @@ export function renderFleetOverlayWrite(priorBytes, write) {
|
|
|
199
215
|
// Whole fleet in: no restriction to express — the allow block goes away entirely.
|
|
200
216
|
deleteAt(doc, ["routing", "allow"]);
|
|
201
217
|
}
|
|
202
|
-
//
|
|
203
|
-
// re-exclude behind the
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
218
|
+
// Authored and non-canonical entries stay in deny (LEG2-T3 finding 4, round 2 finding 2); a
|
|
219
|
+
// list left with nothing is tombstoned so a lower layer can never re-exclude behind the
|
|
220
|
+
// operator's back (workers untouched).
|
|
221
|
+
for (const [scope, after] of [["adapters", edited.denyAdapters], ["models", edited.denyModels]]) {
|
|
222
|
+
const path = ["routing", "deny", scope];
|
|
223
|
+
const remaining = flatDenyAfterWrite(doc, path, after, write.universe);
|
|
224
|
+
setStringSequencePreservingComments(doc, path, remaining.length ? remaining : null);
|
|
225
|
+
}
|
|
208
226
|
}
|
|
209
227
|
else {
|
|
210
228
|
setStringSequencePreservingComments(doc, ["routing", "deny", "adapters"], edited.denyAdapters.length ? sortedUnique(edited.denyAdapters) : null);
|
|
211
229
|
setStringSequencePreservingComments(doc, ["routing", "deny", "models"], edited.denyModels.length ? sortedUnique(edited.denyModels) : null);
|
|
212
230
|
}
|
|
213
231
|
}
|
|
232
|
+
// OBS-994/FL-1: routing.deny.workers is a literal deny list, never a universe-derived
|
|
233
|
+
// membership scope — it never routes through the allow-complement dance above, in either
|
|
234
|
+
// branch. Same tombstone/comment-preserving rules as the flat scopes.
|
|
235
|
+
const initialWorkersAdapters = initial.denyWorkersAdapters ?? [];
|
|
236
|
+
const editedWorkersAdapters = edited.denyWorkersAdapters ?? [];
|
|
237
|
+
const initialWorkersModels = initial.denyWorkersModels ?? [];
|
|
238
|
+
const editedWorkersModels = edited.denyWorkersModels ?? [];
|
|
239
|
+
// Each sub-path mutates independently — an untouched sibling must not be rewritten (a `null`
|
|
240
|
+
// tombstone over an absent/untouched sibling would mask a lower layer's own workers scope).
|
|
241
|
+
if (sortedUnique(initialWorkersAdapters).join() !== sortedUnique(editedWorkersAdapters).join()) {
|
|
242
|
+
setStringSequencePreservingComments(doc, ["routing", "deny", "workers", "adapters"], editedWorkersAdapters.length ? sortedUnique(editedWorkersAdapters) : null);
|
|
243
|
+
}
|
|
244
|
+
if (sortedUnique(initialWorkersModels).join() !== sortedUnique(editedWorkersModels).join()) {
|
|
245
|
+
setStringSequencePreservingComments(doc, ["routing", "deny", "workers", "models"], editedWorkersModels.length ? sortedUnique(editedWorkersModels) : null);
|
|
246
|
+
}
|
|
214
247
|
for (const shape of new Set([...Object.keys(initial.map), ...Object.keys(edited.map)])) {
|
|
215
248
|
const before = initial.map[shape];
|
|
216
249
|
const after = edited.map[shape];
|
|
@@ -379,6 +412,30 @@ export function fleetRepoOverlayFromDelta(initial, edited, existingRepo = {}, fi
|
|
|
379
412
|
}
|
|
380
413
|
routingTouched = true;
|
|
381
414
|
}
|
|
415
|
+
// OBS-994/FL-1: workers deny is a literal list, independent of the universe/allow dance above.
|
|
416
|
+
// Each sub-path is included only when it actually changed — an untouched sibling must not be
|
|
417
|
+
// rewritten as a `null` tombstone over whatever the existing repo overlay already held.
|
|
418
|
+
const initialWorkersAdapters = initial.denyWorkersAdapters ?? [];
|
|
419
|
+
const editedWorkersAdapters = edited.denyWorkersAdapters ?? [];
|
|
420
|
+
const initialWorkersModels = initial.denyWorkersModels ?? [];
|
|
421
|
+
const editedWorkersModels = edited.denyWorkersModels ?? [];
|
|
422
|
+
const workersAdaptersChanged = sortedUnique(initialWorkersAdapters).join() !== sortedUnique(editedWorkersAdapters).join();
|
|
423
|
+
const workersModelsChanged = sortedUnique(initialWorkersModels).join() !== sortedUnique(editedWorkersModels).join();
|
|
424
|
+
if (workersAdaptersChanged || workersModelsChanged) {
|
|
425
|
+
routing.deny = {
|
|
426
|
+
...routing.deny,
|
|
427
|
+
workers: {
|
|
428
|
+
...(routing.deny?.workers),
|
|
429
|
+
...(workersAdaptersChanged
|
|
430
|
+
? { adapters: editedWorkersAdapters.length ? editedWorkersAdapters : null }
|
|
431
|
+
: {}),
|
|
432
|
+
...(workersModelsChanged
|
|
433
|
+
? { models: editedWorkersModels.length ? editedWorkersModels : null }
|
|
434
|
+
: {}),
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
routingTouched = true;
|
|
438
|
+
}
|
|
382
439
|
// pool widened to accept the null tombstone; MapEntry itself never carries null in memory.
|
|
383
440
|
const mapDelta = {};
|
|
384
441
|
for (const shape of new Set([...Object.keys(initial.map), ...Object.keys(edited.map)])) {
|
|
@@ -21,3 +21,10 @@ export type FleetWhyOptions = {
|
|
|
21
21
|
export declare function projectFleetWhy<Id extends string>(values: readonly FleetWhyValue<Id>[], options: FleetWhyOptions): FleetWhyRow<Id>[];
|
|
22
22
|
/** Plain line-mode twin of the Shapes rows; labels are projected, never reconstructed here. */
|
|
23
23
|
export declare function renderFleetWhy(rows: readonly FleetWhyRow[]): string;
|
|
24
|
+
/** LEG2-T3: one exclusion-collector scope as the reason a fleet row shows — its config path, then
|
|
25
|
+
* the entry that matched (a deny) or the fact the allowlist does not admit the channel. */
|
|
26
|
+
export declare function exclusionReason(scope: {
|
|
27
|
+
by: "deny" | "allow";
|
|
28
|
+
configPath: string;
|
|
29
|
+
entry: string;
|
|
30
|
+
}): string;
|
package/dist/config/fleet-why.js
CHANGED
|
@@ -40,3 +40,8 @@ export function projectFleetWhy(values, options) {
|
|
|
40
40
|
export function renderFleetWhy(rows) {
|
|
41
41
|
return ["tickmarkr fleet --why — effective shape routing", ...rows.map((row) => row.label)].join("\n");
|
|
42
42
|
}
|
|
43
|
+
/** LEG2-T3: one exclusion-collector scope as the reason a fleet row shows — its config path, then
|
|
44
|
+
* the entry that matched (a deny) or the fact the allowlist does not admit the channel. */
|
|
45
|
+
export function exclusionReason(scope) {
|
|
46
|
+
return scope.by === "allow" ? `${scope.configPath} (not admitted)` : `${scope.configPath} (${scope.entry})`;
|
|
47
|
+
}
|
package/dist/drivers/index.d.ts
CHANGED
|
@@ -2,6 +2,20 @@ import type { TickmarkrConfig } from "../config/config.js";
|
|
|
2
2
|
import type { ExecutorDriver } from "./types.js";
|
|
3
3
|
export declare const DRIVER_CHOICES: readonly ["auto", "herdr", "subprocess", "orca"];
|
|
4
4
|
export type DriverChoice = (typeof DRIVER_CHOICES)[number];
|
|
5
|
+
export type ClassifiedHost = "herdr" | "orca" | "none";
|
|
6
|
+
/**
|
|
7
|
+
* One host classifier beside the driver chooser reads the launching environment:
|
|
8
|
+
* herdr only when HERDR_ENV is exactly 1, orca only when both Orca markers are present,
|
|
9
|
+
* none otherwise. run and resume call it once at the refs-preflight point and thread the
|
|
10
|
+
* result into pickDriver; nothing downstream reads the markers again.
|
|
11
|
+
*/
|
|
12
|
+
export declare function classifyHost(env?: NodeJS.ProcessEnv): ClassifiedHost;
|
|
13
|
+
/**
|
|
14
|
+
* A config driver of herdr or orca whose host is not the classified one is refused naming the
|
|
15
|
+
* detected host, the config line and the --driver remedy. Any explicit --driver value bypasses
|
|
16
|
+
* this; auto and subprocess are never refused.
|
|
17
|
+
*/
|
|
18
|
+
export declare function preflightHostDriver(cfg: TickmarkrConfig, driverOverride: string | undefined, host: ClassifiedHost): void;
|
|
5
19
|
/**
|
|
6
20
|
* Orca authors both markers on every terminal it creates. Requiring the pair avoids treating an
|
|
7
21
|
* unrelated TERM_PROGRAM value or a copied terminal handle as host identity. This is deliberately
|
|
@@ -11,4 +25,4 @@ export declare function orcaHostDetected(env?: NodeJS.ProcessEnv): boolean;
|
|
|
11
25
|
/** Validate argv at the CLI boundary rather than casting an arbitrary string into a driver choice. */
|
|
12
26
|
export declare function parseDriverOverride(override?: string): DriverChoice | undefined;
|
|
13
27
|
export declare function driverEvidence(cfg: TickmarkrConfig, driver: ExecutorDriver, override?: string): string;
|
|
14
|
-
export declare function pickDriver(cfg: TickmarkrConfig, override?: string): ExecutorDriver;
|
|
28
|
+
export declare function pickDriver(cfg: TickmarkrConfig, override?: string, host?: ClassifiedHost): ExecutorDriver;
|
package/dist/drivers/index.js
CHANGED
|
@@ -3,13 +3,42 @@ import { OrcaDriver } from "./orca.js";
|
|
|
3
3
|
import { SubprocessDriver } from "./subprocess.js";
|
|
4
4
|
export const DRIVER_CHOICES = ["auto", "herdr", "subprocess", "orca"];
|
|
5
5
|
const overrideByDriver = new WeakMap();
|
|
6
|
+
// The host snapshot the driver was selected under: driverEvidence reads this, never process.env,
|
|
7
|
+
// so preflight, selection and the journal row all describe the same instant.
|
|
8
|
+
const hostByDriver = new WeakMap();
|
|
9
|
+
/**
|
|
10
|
+
* One host classifier beside the driver chooser reads the launching environment:
|
|
11
|
+
* herdr only when HERDR_ENV is exactly 1, orca only when both Orca markers are present,
|
|
12
|
+
* none otherwise. run and resume call it once at the refs-preflight point and thread the
|
|
13
|
+
* result into pickDriver; nothing downstream reads the markers again.
|
|
14
|
+
*/
|
|
15
|
+
export function classifyHost(env = process.env) {
|
|
16
|
+
if (env.HERDR_ENV === "1")
|
|
17
|
+
return "herdr";
|
|
18
|
+
if (env.TERM_PROGRAM === "Orca" && env.ORCA_TERMINAL_HANDLE !== undefined && env.ORCA_TERMINAL_HANDLE !== "")
|
|
19
|
+
return "orca";
|
|
20
|
+
return "none";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A config driver of herdr or orca whose host is not the classified one is refused naming the
|
|
24
|
+
* detected host, the config line and the --driver remedy. Any explicit --driver value bypasses
|
|
25
|
+
* this; auto and subprocess are never refused.
|
|
26
|
+
*/
|
|
27
|
+
export function preflightHostDriver(cfg, driverOverride, host) {
|
|
28
|
+
if (driverOverride !== undefined)
|
|
29
|
+
return;
|
|
30
|
+
if ((cfg.driver === "herdr" || cfg.driver === "orca") && cfg.driver !== host) {
|
|
31
|
+
const remedy = host === "none" ? "subprocess" : host;
|
|
32
|
+
throw new Error(`refusing driver '${cfg.driver}' (config line 'driver: ${cfg.driver}'): detected host is ${host}; use --driver ${remedy} to override`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
6
35
|
/**
|
|
7
36
|
* Orca authors both markers on every terminal it creates. Requiring the pair avoids treating an
|
|
8
37
|
* unrelated TERM_PROGRAM value or a copied terminal handle as host identity. This is deliberately
|
|
9
38
|
* environment-only: selection must not execute a binary or contact the Orca runtime.
|
|
10
39
|
*/
|
|
11
40
|
export function orcaHostDetected(env = process.env) {
|
|
12
|
-
return env
|
|
41
|
+
return classifyHost(env) === "orca";
|
|
13
42
|
}
|
|
14
43
|
/** Validate argv at the CLI boundary rather than casting an arbitrary string into a driver choice. */
|
|
15
44
|
export function parseDriverOverride(override) {
|
|
@@ -27,18 +56,16 @@ export function driverEvidence(cfg, driver, override) {
|
|
|
27
56
|
return `${driver.id} (--driver)`;
|
|
28
57
|
if (want !== "auto")
|
|
29
58
|
return `${driver.id} (config)`;
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
59
|
+
const host = hostByDriver.get(driver);
|
|
60
|
+
if (host === "herdr" && driver.id === "herdr")
|
|
32
61
|
return "auto → herdr (HERDR_ENV=1)";
|
|
33
|
-
if (
|
|
62
|
+
if (host === "orca" && driver.id === "orca")
|
|
34
63
|
return "auto → orca (TERM_PROGRAM+ORCA_TERMINAL_HANDLE)";
|
|
35
|
-
|
|
36
|
-
if (!herdrAvailable && !orcaHostDetected() && driver.id === "subprocess") {
|
|
64
|
+
if (host === "none" && driver.id === "subprocess")
|
|
37
65
|
return "auto → subprocess (HERDR_ENV unset)";
|
|
38
|
-
}
|
|
39
66
|
return `auto → ${driver.id} (runtime)`;
|
|
40
67
|
}
|
|
41
|
-
export function pickDriver(cfg, override) {
|
|
68
|
+
export function pickDriver(cfg, override, host = classifyHost()) {
|
|
42
69
|
const selectedOverride = parseDriverOverride(override);
|
|
43
70
|
const want = selectedOverride ?? cfg.driver;
|
|
44
71
|
// VIS-09 item 2: plumb the per-tab cap into the HerdrDriver — the driver takes it as a constructor
|
|
@@ -49,10 +76,11 @@ export function pickDriver(cfg, override) {
|
|
|
49
76
|
// Orca is an operator-selected execution surface. Its runtime failure stays on Orca; selection
|
|
50
77
|
// must never substitute a hidden subprocess worker after an explicit or detected choice.
|
|
51
78
|
: want === "orca" ? new OrcaDriver()
|
|
52
|
-
:
|
|
53
|
-
:
|
|
79
|
+
: host === "herdr" ? new HerdrDriver("herdr", cfg.visibility.workersPerTab)
|
|
80
|
+
: host === "orca" ? new OrcaDriver()
|
|
54
81
|
: new SubprocessDriver();
|
|
55
82
|
if (selectedOverride !== undefined)
|
|
56
83
|
overrideByDriver.set(driver, selectedOverride);
|
|
84
|
+
hostByDriver.set(driver, host);
|
|
57
85
|
return driver;
|
|
58
86
|
}
|
package/dist/drivers/orca.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type ShResult } from "../run/git.js";
|
|
2
2
|
import { type JournalEvent } from "../run/journal.js";
|
|
3
|
+
import { type WatchBoardOwner } from "../run/supervision.js";
|
|
3
4
|
import { type ExecutorDriver, type FocusTarget, type FocusResult, type NotifyOpts, type Slot, type SlotOpts } from "./types.js";
|
|
4
5
|
/** The response families the ONE shared envelope parser serves. There is no second JSON seam. */
|
|
5
|
-
export declare const ORCA_RESPONSE_FAMILIES: readonly ["status", "create", "list", "read", "send", "wait", "show", "close", "worktree-current", "worktree-set", "hooks-status"];
|
|
6
|
+
export declare const ORCA_RESPONSE_FAMILIES: readonly ["status", "create", "list", "read", "send", "wait", "show", "close", "worktree-current", "worktree-set", "hooks-status", "split"];
|
|
6
7
|
export type OrcaFamily = (typeof ORCA_RESPONSE_FAMILIES)[number];
|
|
7
8
|
export declare const ORCA_FIXTURE_VERSION = "1.4.195";
|
|
8
9
|
export declare const ORCA_CLI_COMMAND_ENV = "ORCA_CLI_COMMAND";
|
|
@@ -14,7 +15,6 @@ export declare const NOT_WRITABLE_CODE = "terminal_not_writable";
|
|
|
14
15
|
/** The ONLY terminal status that licenses reading a terminal's bytes or its agent state. */
|
|
15
16
|
export declare const RUNNING_STATUS = "running";
|
|
16
17
|
export declare const STATUS_GOVERNED_METHODS: readonly ["read", "waitOutput", "status", "waitAgentStatus"];
|
|
17
|
-
export declare const WORKTREE_ADOPTION_TIMEOUT_MS = 60000;
|
|
18
18
|
/** A missing slot gets the same bounded chance to appear as a reaped shell gets to settle. */
|
|
19
19
|
export declare const PENDING_PROJECT_GRACE_MS = 2000;
|
|
20
20
|
export interface OrcaExec {
|
|
@@ -81,11 +81,62 @@ export declare function terminalWorktree(term: Record<string, unknown>): string
|
|
|
81
81
|
* it keeps its resolved spelling: deterministic, and still comparable to another spelling of itself.
|
|
82
82
|
*/
|
|
83
83
|
export declare function canonicalWorktreePath(path: string): string;
|
|
84
|
+
/** The proof line a worker terminal prints first: recovery and focus read it back (FX-N01). */
|
|
85
|
+
export declare const CHECKOUT_MARK = "TICKMARKR_CHECKOUT";
|
|
86
|
+
/** The exact bytes the create command prints as its first line. */
|
|
87
|
+
export declare function checkoutProofLine(checkout: string): string;
|
|
88
|
+
/** Every complete frame in a scrollback, decoded and canonicalized; whether an incomplete one was seen. */
|
|
89
|
+
export declare function checkoutFrames(text: string): {
|
|
90
|
+
complete: string[];
|
|
91
|
+
incomplete: boolean;
|
|
92
|
+
};
|
|
93
|
+
/** Does a scrollback prove exactly `checkout`: at least one complete frame equals it, no complete
|
|
94
|
+
* frame names anything else, and no frame is incomplete. Full-path equality after canonicalization —
|
|
95
|
+
* never a prefix, a substring, or a whitespace-terminated fragment. */
|
|
96
|
+
export declare function provesCheckout(text: string, checkout: string): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Everything a terminal on the tracked worktree runs before the payload: enter the checkout (a
|
|
99
|
+
* failed cd stops the whole line — nothing of the payload ever runs in the enclosing path), print
|
|
100
|
+
* the proof line, then hand the WHOLE payload to one `sh -c` so a background list, a `;` list or a
|
|
101
|
+
* subshell inside it all start in the checkout and its exit status is the payload's (FX-N02).
|
|
102
|
+
*/
|
|
103
|
+
export declare function checkoutPrefix(checkout: string): string;
|
|
104
|
+
/** The command a terminal on the tracked worktree runs so that it executes INSIDE the checkout. */
|
|
105
|
+
export declare function inCheckout(checkout: string, cmd: string): string;
|
|
106
|
+
/** Every checkout the complete proof frames in a scrollback name, in order of appearance. */
|
|
107
|
+
export declare function checkoutsNamed(text: string): string[];
|
|
84
108
|
/** Conservative agent-state mapping over orca's ACTUAL surfaces: `blocked` only when the show
|
|
85
109
|
* record reports agentWait:true, `idle` only when the `terminal wait --for tui-idle` condition is
|
|
86
110
|
* satisfied. The recorded 1.4.186 show response carries NO agent field at all — an absent signal
|
|
87
111
|
* is "unknown", never a fabricated definite status. */
|
|
88
112
|
export declare function mapAgentState(term: Record<string, unknown>, tuiIdle: boolean): string;
|
|
113
|
+
/**
|
|
114
|
+
* The Orca board owner record is the board's ONE lifecycle, and it lives on disk: every step below is
|
|
115
|
+
* decided from the file (plus Orca's own terminal table), so a fresh OrcaDriver — a restarted daemon —
|
|
116
|
+
* reaches the same answer as the instance that placed the board. No instance map or set carries it.
|
|
117
|
+
*
|
|
118
|
+
* reserved pane "", no claim narrator, create-only, before the split can read its token
|
|
119
|
+
* claimed pid + armId, pane "" observer (observeNamedRun), written exactly once
|
|
120
|
+
* bound claim + the receipt's handle and the split envelope's runtimeId
|
|
121
|
+
* retired bound + retired:true tombstone, CAS on the bound bytes; never answered again
|
|
122
|
+
*
|
|
123
|
+
* A retired record is replaced by a new reservation (CAS on the tombstone bytes) only once its pane
|
|
124
|
+
* is proven gone. Everything else — a reservation or claim with no bound pane, a failed cleanup, a
|
|
125
|
+
* record another driver holds with a live observer — refuses and keeps the record exactly as it is.
|
|
126
|
+
*/
|
|
127
|
+
type BoardRecord = WatchBoardOwner & {
|
|
128
|
+
retired?: true;
|
|
129
|
+
runtimeId?: string;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Whole-record compare-and-swap. The canonical path stays readable until commit: a mkdir lock
|
|
133
|
+
* excludes other writers, then create-only `link`s the new inode (fails if anything exists) or
|
|
134
|
+
* `rename`s the new file over the live path (POSIX atomic replace — readers see old or new, never
|
|
135
|
+
* absence). A crash that leaves a `.tmp` or `.lock` does not drop the previous record.
|
|
136
|
+
* ponytail: observeNamedRun (supervision.ts) renames without CAS. It cannot interleave with a swap
|
|
137
|
+
* because the narrator writes nothing between reserve and claim, and no transition here swaps one.
|
|
138
|
+
*/
|
|
139
|
+
export declare function casBoard(family: string, path: string, expected: string | undefined, next: BoardRecord): string;
|
|
89
140
|
/**
|
|
90
141
|
* The renderer hard-wraps long lines, paints margin chrome, and a cursor page boundary splits a
|
|
91
142
|
* marker exactly like a wrap does. `parseWorkerResult` (src/adapters/prompt.ts) already de-wraps
|
|
@@ -105,6 +156,7 @@ export interface OrcaDriverOpts {
|
|
|
105
156
|
pollMs?: number;
|
|
106
157
|
/** Bounded, seam-adjustable staleness window for runtime probes before mutations. */
|
|
107
158
|
probeStalenessMs?: number;
|
|
159
|
+
launchingHandle?: string;
|
|
108
160
|
}
|
|
109
161
|
export declare class OrcaDriver implements ExecutorDriver {
|
|
110
162
|
id: string;
|
|
@@ -121,7 +173,12 @@ export declare class OrcaDriver implements ExecutorDriver {
|
|
|
121
173
|
private narrate?;
|
|
122
174
|
private hookCoverage?;
|
|
123
175
|
private taskWorktrees;
|
|
176
|
+
private trackedByCheckout;
|
|
124
177
|
private pendingProjects;
|
|
178
|
+
private env;
|
|
179
|
+
private launchingHandle?;
|
|
180
|
+
private serialQueue;
|
|
181
|
+
private serial;
|
|
125
182
|
constructor(opts?: OrcaDriverOpts);
|
|
126
183
|
private call;
|
|
127
184
|
/** The live runtime's identity, or an explicit failure. A missing or unreachable runtime is a
|
|
@@ -144,14 +201,13 @@ export declare class OrcaDriver implements ExecutorDriver {
|
|
|
144
201
|
private sendReceipt;
|
|
145
202
|
private create;
|
|
146
203
|
/**
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
204
|
+
* OBS-1004: the tracked worktree that encloses a checkout, asked ONCE of `worktree current` from
|
|
205
|
+
* inside that checkout. Orca answers the exact path when it tracks the checkout itself, the
|
|
206
|
+
* enclosing tracked clone for a git worktree the daemon added beneath it (1.4.200, verified from
|
|
207
|
+
* `.tickmarkr/worktrees.noindex/<task>`), and selector_not_found when nothing it tracks encloses
|
|
208
|
+
* the cwd — which is a driver failure, not something to wait out: Orca has no adopt verb.
|
|
151
209
|
*/
|
|
152
|
-
private
|
|
153
|
-
/** Same repo/run/narration path Herdr uses for its driver-owned dispatch-retry row. */
|
|
154
|
-
private appendAdoptionWait;
|
|
210
|
+
private trackedWorktree;
|
|
155
211
|
/**
|
|
156
212
|
* Every terminal-addressed call — read AND write — goes through here, and the runtime identity is
|
|
157
213
|
* established BEFORE the runtime-scoped handle goes on the wire. Discarding a lookalike's answer
|
|
@@ -161,6 +217,19 @@ export declare class OrcaDriver implements ExecutorDriver {
|
|
|
161
217
|
* the handle exactly once, then re-issues the operation against the replacement.
|
|
162
218
|
*/
|
|
163
219
|
private terminalOp;
|
|
220
|
+
/**
|
|
221
|
+
* FX-N01/N05/N06: under a shared enclosing worktree every task terminal lists with the same
|
|
222
|
+
* worktreePath, so the tracked path + owned title cannot tell two nested checkouts apart. The
|
|
223
|
+
* runtime's own proof is the terminal's earliest scrollback, where the create command printed a
|
|
224
|
+
* framed `TICKMARKR_CHECKOUT` line before its payload (checkoutProofLine). READ-only calls: the
|
|
225
|
+
* anchor (for `oldestCursor`), then pages from the oldest cursor until a frame is complete or the
|
|
226
|
+
* bound is hit. Every page is evidence only when the response's own identity is the candidate's:
|
|
227
|
+
* the terminal record must name `handle` and `_meta.runtimeId` must be `runtimeId` — the runtime
|
|
228
|
+
* that supplied the ownership listing — else another terminal's or another runtime's bytes were
|
|
229
|
+
* answered and nothing is proven. Proven means provesCheckout: exact canonical full-path equality
|
|
230
|
+
* of a complete frame, no other checkout named, no incomplete frame.
|
|
231
|
+
*/
|
|
232
|
+
private checkoutProven;
|
|
164
233
|
private recover;
|
|
165
234
|
/** Validated READ terminal record, or an explicit unavailable failure. Called BEFORE any caller
|
|
166
235
|
* looks at tail bytes — on every page, on every read-governed method. Read records are the one
|
|
@@ -200,7 +269,24 @@ export declare class OrcaDriver implements ExecutorDriver {
|
|
|
200
269
|
waitAgentStatus(slot: Slot, status: string, timeoutMs: number): Promise<boolean>;
|
|
201
270
|
sendKey(slot: Slot, key: string): Promise<void>;
|
|
202
271
|
nudge(slot: Slot, message: string): Promise<boolean>;
|
|
203
|
-
narrator(
|
|
272
|
+
narrator(cwd: string, command: string, runId?: string): Promise<Slot>;
|
|
273
|
+
private boardPath;
|
|
274
|
+
/** A recorded pane (a receipt's handle bound to the split envelope's runtime, never a guess) is
|
|
275
|
+
* gone when that runtime no longer lists it, or when a handle-bound close receipt names it.
|
|
276
|
+
* A handle listed by a different runtime is a different pane — not closed, treated as gone. */
|
|
277
|
+
private closeRecordedPane;
|
|
278
|
+
/** bound → retired, decided from the record alone: it must be this driver's board for exactly this
|
|
279
|
+
* slot's pane. Already retired is returned as it is. */
|
|
280
|
+
private retireBoard;
|
|
281
|
+
/** bound → retired first: whatever fails below, no later call answers this board again. A live
|
|
282
|
+
* observer is asked to stop and its acknowledgement awaited on injected time before the
|
|
283
|
+
* handle-bound close (timeout keeps the tombstone and the pane); a dead one never acknowledges,
|
|
284
|
+
* so it is only asked. */
|
|
285
|
+
private retireAndClose;
|
|
286
|
+
/** WB-1 seam: the daemon reports this board lost. "Lost" can be a stale beat or missing presence
|
|
287
|
+
* under a still-live owner pid, so it is not proof of a dead observer — retirement keeps close's
|
|
288
|
+
* acknowledgement discipline (Leg-2 T9 P1). */
|
|
289
|
+
retireLostWatch(slot: Slot): Promise<void>;
|
|
204
290
|
focus(target: FocusTarget): Promise<FocusResult>;
|
|
205
291
|
project(taskId: string, state: "in-progress" | "in-review" | "completed"): Promise<void>;
|
|
206
292
|
private setWorkspaceStatus;
|
|
@@ -243,5 +329,8 @@ export declare class OrcaDriver implements ExecutorDriver {
|
|
|
243
329
|
reconcile(desired: Set<string>, runId: string, opts?: {
|
|
244
330
|
spareLiveLlm?: boolean;
|
|
245
331
|
}): Promise<void>;
|
|
332
|
+
private isRecordedWatchHandle;
|
|
333
|
+
private isRecordedWorkerHandle;
|
|
246
334
|
worktree(repo: string, branch: string, baseRef: string): Promise<string>;
|
|
247
335
|
}
|
|
336
|
+
export {};
|