tickmarkr 2.0.0 → 2.1.1

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.
@@ -0,0 +1,189 @@
1
+ import { type ShResult } from "../run/git.js";
2
+ import { type ExecutorDriver, type NotifyOpts, type Slot, type SlotOpts } from "./types.js";
3
+ /** The response families the ONE shared envelope parser serves. There is no second JSON seam. */
4
+ export declare const ORCA_RESPONSE_FAMILIES: readonly ["status", "create", "list", "read", "send", "wait", "show", "close"];
5
+ export type OrcaFamily = (typeof ORCA_RESPONSE_FAMILIES)[number];
6
+ export declare const STALE_HANDLE_CODE = "terminal_handle_stale";
7
+ export declare const NOT_WRITABLE_CODE = "terminal_not_writable";
8
+ /** The ONLY terminal status that licenses reading a terminal's bytes or its agent state. */
9
+ export declare const RUNNING_STATUS = "running";
10
+ export declare const STATUS_GOVERNED_METHODS: readonly ["read", "waitOutput", "status", "waitAgentStatus"];
11
+ export interface OrcaExec {
12
+ (args: string[], cwd: string, timeoutMs?: number): Promise<ShResult>;
13
+ }
14
+ export interface OrcaTimeSource {
15
+ now: () => number;
16
+ sleep: (ms: number) => Promise<void>;
17
+ }
18
+ /** Every failure this driver produces is explicit and carries the raw bytes that produced it. */
19
+ export declare class OrcaError extends Error {
20
+ readonly family: string;
21
+ readonly reason: string;
22
+ readonly raw: string;
23
+ readonly code?: string;
24
+ readonly runtimeId?: string;
25
+ constructor(family: string, reason: string, raw: string, opts?: {
26
+ code?: string;
27
+ runtimeId?: string;
28
+ });
29
+ }
30
+ /** The slot cannot be addressed: dead/unknown terminal record, or a handle that cannot be recovered
31
+ * to exactly one owned terminal in the slot's own worktree. Never a silent false or empty string. */
32
+ export declare class OrcaUnavailableError extends OrcaError {
33
+ readonly terminalStatus?: string | undefined;
34
+ constructor(family: string, reason: string, raw: string, terminalStatus?: string | undefined);
35
+ }
36
+ export interface OrcaEnvelope {
37
+ result: Record<string, unknown>;
38
+ runtimeId: string;
39
+ raw: string;
40
+ }
41
+ /**
42
+ * The one JSON seam. Fails CLOSED on every degenerate response — empty, unparseable (a truncated
43
+ * body lands here), non-object, no boolean `ok`, `ok:false`, `ok:true` with no result object, or a
44
+ * successful response without a usable `_meta.runtimeId` —
45
+ * and preserves the raw bytes on the thrown error for diagnostics. Callers never see a partial
46
+ * envelope, so no caller can reinterpret a parse failure as empty output, an unknown-but-successful
47
+ * status, or a successful close.
48
+ */
49
+ export declare function parseEnvelope(family: OrcaFamily, stdout: string, raw?: string): OrcaEnvelope;
50
+ /** The worktree a terminal record binds to. The spike pinned the record's shape but not this key's
51
+ * spelling, so the known aliases are accepted and nothing else — a record with none is unbound,
52
+ * which fails every identity comparison below rather than passing one by default. */
53
+ export declare function terminalWorktree(term: Record<string, unknown>): string | undefined;
54
+ /**
55
+ * The same checkout under two spellings. git hands tickmarkr one (`/tmp/...` on darwin, or anything
56
+ * below a symlinked parent) while Orca answers the canonicalized one (`/private/tmp/...`), and
57
+ * `resolve()` collapses `..` but never a symlink — so string equality on resolved paths reports two
58
+ * different checkouts and leaves a perfectly valid slot unreacquirable after a runtime restart.
59
+ * Identity is FILESYSTEM identity. A path that does not exist has no filesystem identity to read, so
60
+ * it keeps its resolved spelling: deterministic, and still comparable to another spelling of itself.
61
+ */
62
+ export declare function canonicalWorktreePath(path: string): string;
63
+ /** Conservative agent-state mapping over orca's ACTUAL surfaces: `blocked` only when the show
64
+ * record reports agentWait:true, `idle` only when the `terminal wait --for tui-idle` condition is
65
+ * satisfied. The recorded 1.4.186 show response carries NO agent field at all — an absent signal
66
+ * is "unknown", never a fabricated definite status. */
67
+ export declare function mapAgentState(term: Record<string, unknown>, tuiIdle: boolean): string;
68
+ /**
69
+ * The renderer hard-wraps long lines, paints margin chrome, and a cursor page boundary splits a
70
+ * marker exactly like a wrap does. `parseWorkerResult` (src/adapters/prompt.ts) already de-wraps
71
+ * trailers this way, so marker matching gets the same joined view beside the raw one.
72
+ * ponytail: joining every line can in principle glue two unrelated lines into a marker — the same
73
+ * tolerance parseWorkerResult has carried since v1.2; raw is matched first, so an unwrapped hit
74
+ * never depends on this.
75
+ */
76
+ export declare function joinWrapped(raw: string): string;
77
+ export interface OrcaDriverOpts {
78
+ bin?: string;
79
+ exec?: OrcaExec;
80
+ time?: OrcaTimeSource;
81
+ pageLines?: number;
82
+ pollMs?: number;
83
+ /** Bounded, seam-adjustable staleness window for runtime probes before mutations. */
84
+ probeStalenessMs?: number;
85
+ }
86
+ export declare class OrcaDriver implements ExecutorDriver {
87
+ id: string;
88
+ interactive: boolean;
89
+ private slots;
90
+ private n;
91
+ private bin;
92
+ private exec;
93
+ private time;
94
+ private pageLines;
95
+ private pollMs;
96
+ private probeStalenessMs;
97
+ constructor(opts?: OrcaDriverOpts);
98
+ private call;
99
+ /** The live runtime's identity, or an explicit failure. A missing or unreachable runtime is a
100
+ * driver-level failure carrying the raw refusal — never a reachable-looking default. */
101
+ private runtimeEnv;
102
+ /** Explicit runtime probe. Also T3's doctor probe. */
103
+ probeRuntime(cwd?: string): Promise<string>;
104
+ slot(cwd: string, name: string, opts?: SlotOpts): Promise<Slot>;
105
+ /** Where to invoke the CLI for this slot's calls (see OrcaSlotState.dir). */
106
+ private cliCwd;
107
+ private state;
108
+ private latched;
109
+ private assertAvailable;
110
+ run(slot: Slot, cmd: string): Promise<void>;
111
+ private create;
112
+ /**
113
+ * Every terminal-addressed call — read AND write — goes through here, and the runtime identity is
114
+ * established BEFORE the runtime-scoped handle goes on the wire. Discarding a lookalike's answer
115
+ * after reading it is still having addressed it, so the probe comes first; the post-call check
116
+ * only closes the narrow race of a restart landing between probe and call. Same for an explicit
117
+ * `terminal_handle_stale`. Either way the driver relists the slot's exact worktree and replaces
118
+ * the handle exactly once, then re-issues the operation against the replacement.
119
+ */
120
+ private terminalOp;
121
+ private recover;
122
+ /** Validated READ terminal record, or an explicit unavailable failure. Called BEFORE any caller
123
+ * looks at tail bytes — on every page, on every read-governed method. Read records are the one
124
+ * place orca reports a literal `status` (recorded: "running" live, "exited" on the dead record). */
125
+ private validated;
126
+ /** Validated SHOW terminal record, or an explicit unavailable failure. The recorded 1.4.186 show
127
+ * response reports liveness through connected/orphaned and carries NO status and NO agent field,
128
+ * so this is the status discipline's show leg: a terminal that cannot prove connected-and-not-
129
+ * orphaned is unavailable for state questions, exactly as a non-running read record is for bytes. */
130
+ private liveShowTerm;
131
+ private tailText;
132
+ private readPage;
133
+ /** A single UNPAGED tail read — exactly what the caller asked for and nothing more. Markers split
134
+ * across cursor pages are not reassembled here; that is waitOutput's job. */
135
+ read(slot: Slot, lines: number): Promise<string>;
136
+ /**
137
+ * Bounded cursor-paged sweep into the slot's accumulated buffer. The first read of a slot carries
138
+ * no cursor: it is the ANCHOR, whose `oldestCursor` says where the retained buffer starts (its own
139
+ * tail is the newest lines, not the oldest, so it is not appended). Every page after it appends,
140
+ * and every one of them — anchor included — is status-validated before a single byte is matched.
141
+ */
142
+ private sweep;
143
+ waitOutput(slot: Slot, pattern: string, timeoutMs: number, opts?: {
144
+ regex?: boolean;
145
+ }): Promise<boolean>;
146
+ status(slot: Slot): Promise<string>;
147
+ /** One `terminal wait` through the full identity machinery. The recorded 1.4.186 elapsed answer
148
+ * is rc 1 + ok:true + {handle, condition, satisfied:false, status:"running"}; it is "not yet"
149
+ * only after this method validates all four fields. Any malformed/refused wait remains explicit. */
150
+ private waitCondition;
151
+ waitAgentStatus(slot: Slot, status: string, timeoutMs: number): Promise<boolean>;
152
+ notify(msg: string, opts?: NotifyOpts): Promise<void>;
153
+ close(slot: Slot): Promise<void>;
154
+ /**
155
+ * The one destructive call in this driver, for a slot's own terminal AND for a reconcile candidate
156
+ * alike. It goes through terminalOp deliberately: the live runtime identity is proven immediately
157
+ * BEFORE the handle goes on the wire, and a runtime that changed does not merely fail the close —
158
+ * the handle is DISCARDED and re-derived from the owned tab title in that exact checkout, where a
159
+ * handle value the new runtime happened to reissue to somebody else's terminal is refused by
160
+ * construction (recover()). Checking identity on the receipt afterwards could not undo a close.
161
+ */
162
+ private closeTerminal;
163
+ /**
164
+ * The WHOLE terminal table. `terminal list` caps rows at its own default and says so through
165
+ * `truncated`/`totalCount`; a capped listing is not an ownership snapshot, because the row it
166
+ * dropped is precisely the older run's leftover no later sweep would ever see again.
167
+ * ponytail: two asks, not a paging loop — `--limit` takes the whole table in one go, and a runtime
168
+ * that still reports truncated at totalCount rows is a listing this sweep declines to judge on.
169
+ */
170
+ private listAll;
171
+ /**
172
+ * Sweep tickmarkr-owned terminals down to `desired`. Ownership is decided ONLY by parseOwnedName
173
+ * over the owned TAB title, through the same panesToClose fold herdr uses (drivers/types.ts): an
174
+ * owned-and-undesired terminal closes whichever run — and whichever daemon — created it, and a
175
+ * title that does not parse is never a candidate however much it resembles one.
176
+ *
177
+ * The listing is UNSCOPED and layout-bearing. Unscoped because an older run's leftover sits in a
178
+ * checkout this run never knew, so a `--worktree`-filtered sweep is exactly how such a leftover
179
+ * survives forever. Layout-bearing because the owned title survives at TAB identity only — a list
180
+ * row's `title` is the shell-controlled pane title, and closing on that is how a foreign pane that
181
+ * happens to be running an owned-looking command gets killed.
182
+ *
183
+ * Cosmetic by contract: every failure is swallowed, per candidate and overall.
184
+ */
185
+ reconcile(desired: Set<string>, runId: string, opts?: {
186
+ spareLiveLlm?: boolean;
187
+ }): Promise<void>;
188
+ worktree(repo: string, branch: string, baseRef: string): Promise<string>;
189
+ }