@gigaflow/gmux 0.15.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/CHANGELOG.md +654 -0
- package/LICENSE +21 -0
- package/README.md +351 -0
- package/dist/adapters/claude-code.d.ts +52 -0
- package/dist/adapters/claude-code.js +315 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/codex.d.ts +39 -0
- package/dist/adapters/codex.js +233 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/jsonl.d.ts +40 -0
- package/dist/adapters/jsonl.js +92 -0
- package/dist/adapters/jsonl.js.map +1 -0
- package/dist/adapters/registry.d.ts +11 -0
- package/dist/adapters/registry.js +21 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/types.d.ts +43 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli/border-client.d.ts +8 -0
- package/dist/cli/border-client.js +11 -0
- package/dist/cli/border-client.js.map +1 -0
- package/dist/cli/commands/__ask-cancel.d.ts +35 -0
- package/dist/cli/commands/__ask-cancel.js +91 -0
- package/dist/cli/commands/__ask-cancel.js.map +1 -0
- package/dist/cli/commands/__ask-refresh.d.ts +40 -0
- package/dist/cli/commands/__ask-refresh.js +54 -0
- package/dist/cli/commands/__ask-refresh.js.map +1 -0
- package/dist/cli/commands/__ask-run.d.ts +43 -0
- package/dist/cli/commands/__ask-run.js +126 -0
- package/dist/cli/commands/__ask-run.js.map +1 -0
- package/dist/cli/commands/__ask-send.d.ts +50 -0
- package/dist/cli/commands/__ask-send.js +138 -0
- package/dist/cli/commands/__ask-send.js.map +1 -0
- package/dist/cli/commands/__preview-card.d.ts +31 -0
- package/dist/cli/commands/__preview-card.js +72 -0
- package/dist/cli/commands/__preview-card.js.map +1 -0
- package/dist/cli/commands/ask.d.ts +50 -0
- package/dist/cli/commands/ask.js +240 -0
- package/dist/cli/commands/ask.js.map +1 -0
- package/dist/cli/commands/auto.d.ts +12 -0
- package/dist/cli/commands/auto.js +28 -0
- package/dist/cli/commands/auto.js.map +1 -0
- package/dist/cli/commands/cockpit.d.ts +14 -0
- package/dist/cli/commands/cockpit.js +52 -0
- package/dist/cli/commands/cockpit.js.map +1 -0
- package/dist/cli/commands/daemon.d.ts +75 -0
- package/dist/cli/commands/daemon.js +258 -0
- package/dist/cli/commands/daemon.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +6 -0
- package/dist/cli/commands/doctor.js +159 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/grep.d.ts +2 -0
- package/dist/cli/commands/grep.js +52 -0
- package/dist/cli/commands/grep.js.map +1 -0
- package/dist/cli/commands/index-cmd.d.ts +2 -0
- package/dist/cli/commands/index-cmd.js +29 -0
- package/dist/cli/commands/index-cmd.js.map +1 -0
- package/dist/cli/commands/ls.d.ts +30 -0
- package/dist/cli/commands/ls.js +95 -0
- package/dist/cli/commands/ls.js.map +1 -0
- package/dist/cli/commands/overlay.d.ts +26 -0
- package/dist/cli/commands/overlay.js +216 -0
- package/dist/cli/commands/overlay.js.map +1 -0
- package/dist/cli/commands/pick.d.ts +59 -0
- package/dist/cli/commands/pick.js +254 -0
- package/dist/cli/commands/pick.js.map +1 -0
- package/dist/cli/commands/picker-rows.d.ts +17 -0
- package/dist/cli/commands/picker-rows.js +47 -0
- package/dist/cli/commands/picker-rows.js.map +1 -0
- package/dist/cli/commands/resume.d.ts +25 -0
- package/dist/cli/commands/resume.js +88 -0
- package/dist/cli/commands/resume.js.map +1 -0
- package/dist/cli/commands/run.d.ts +13 -0
- package/dist/cli/commands/run.js +78 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +37 -0
- package/dist/cli/commands/setup.js +200 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/show.d.ts +2 -0
- package/dist/cli/commands/show.js +24 -0
- package/dist/cli/commands/show.js.map +1 -0
- package/dist/cli/commands/summarize.d.ts +2 -0
- package/dist/cli/commands/summarize.js +59 -0
- package/dist/cli/commands/summarize.js.map +1 -0
- package/dist/cli/commands/tmux.d.ts +11 -0
- package/dist/cli/commands/tmux.js +90 -0
- package/dist/cli/commands/tmux.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +2 -0
- package/dist/cli/commands/watch.js +76 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/format.d.ts +74 -0
- package/dist/cli/format.js +242 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/gmux-render.d.ts +11 -0
- package/dist/cli/gmux-render.js +60 -0
- package/dist/cli/gmux-render.js.map +1 -0
- package/dist/cli/main.d.ts +8 -0
- package/dist/cli/main.js +145 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/overlay-ask.d.ts +13 -0
- package/dist/cli/overlay-ask.js +32 -0
- package/dist/cli/overlay-ask.js.map +1 -0
- package/dist/cli/overlay.d.ts +26 -0
- package/dist/cli/overlay.js +149 -0
- package/dist/cli/overlay.js.map +1 -0
- package/dist/cli/picker.d.ts +308 -0
- package/dist/cli/picker.js +642 -0
- package/dist/cli/picker.js.map +1 -0
- package/dist/cli/preview.d.ts +87 -0
- package/dist/cli/preview.js +292 -0
- package/dist/cli/preview.js.map +1 -0
- package/dist/cli/tmux-label.d.ts +50 -0
- package/dist/cli/tmux-label.js +146 -0
- package/dist/cli/tmux-label.js.map +1 -0
- package/dist/core/errors.d.ts +54 -0
- package/dist/core/errors.js +91 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/fingerprint.d.ts +21 -0
- package/dist/core/fingerprint.js +59 -0
- package/dist/core/fingerprint.js.map +1 -0
- package/dist/core/gmux-types.d.ts +102 -0
- package/dist/core/gmux-types.js +13 -0
- package/dist/core/gmux-types.js.map +1 -0
- package/dist/core/pane-state.d.ts +5 -0
- package/dist/core/pane-state.js +15 -0
- package/dist/core/pane-state.js.map +1 -0
- package/dist/core/paths.d.ts +68 -0
- package/dist/core/paths.js +106 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/proc-tree.d.ts +9 -0
- package/dist/core/proc-tree.js +33 -0
- package/dist/core/proc-tree.js.map +1 -0
- package/dist/core/text.d.ts +34 -0
- package/dist/core/text.js +126 -0
- package/dist/core/text.js.map +1 -0
- package/dist/core/types.d.ts +322 -0
- package/dist/core/types.js +10 -0
- package/dist/core/types.js.map +1 -0
- package/dist/services/ask-transcript.d.ts +256 -0
- package/dist/services/ask-transcript.js +544 -0
- package/dist/services/ask-transcript.js.map +1 -0
- package/dist/services/ask.d.ts +54 -0
- package/dist/services/ask.js +161 -0
- package/dist/services/ask.js.map +1 -0
- package/dist/services/auto-summarize.d.ts +188 -0
- package/dist/services/auto-summarize.js +415 -0
- package/dist/services/auto-summarize.js.map +1 -0
- package/dist/services/concurrency.d.ts +2 -0
- package/dist/services/concurrency.js +16 -0
- package/dist/services/concurrency.js.map +1 -0
- package/dist/services/config.d.ts +131 -0
- package/dist/services/config.js +325 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/daemon-client.d.ts +9 -0
- package/dist/services/daemon-client.js +34 -0
- package/dist/services/daemon-client.js.map +1 -0
- package/dist/services/daemon-socket.d.ts +30 -0
- package/dist/services/daemon-socket.js +76 -0
- package/dist/services/daemon-socket.js.map +1 -0
- package/dist/services/daemon.d.ts +43 -0
- package/dist/services/daemon.js +106 -0
- package/dist/services/daemon.js.map +1 -0
- package/dist/services/distill.d.ts +39 -0
- package/dist/services/distill.js +93 -0
- package/dist/services/distill.js.map +1 -0
- package/dist/services/guardian.d.ts +26 -0
- package/dist/services/guardian.js +71 -0
- package/dist/services/guardian.js.map +1 -0
- package/dist/services/index-store.d.ts +36 -0
- package/dist/services/index-store.js +125 -0
- package/dist/services/index-store.js.map +1 -0
- package/dist/services/log-rotation.d.ts +3 -0
- package/dist/services/log-rotation.js +28 -0
- package/dist/services/log-rotation.js.map +1 -0
- package/dist/services/pane-links.d.ts +10 -0
- package/dist/services/pane-links.js +47 -0
- package/dist/services/pane-links.js.map +1 -0
- package/dist/services/pane-process.d.ts +95 -0
- package/dist/services/pane-process.js +183 -0
- package/dist/services/pane-process.js.map +1 -0
- package/dist/services/pane-registry.d.ts +24 -0
- package/dist/services/pane-registry.js +40 -0
- package/dist/services/pane-registry.js.map +1 -0
- package/dist/services/provider-process.d.ts +37 -0
- package/dist/services/provider-process.js +105 -0
- package/dist/services/provider-process.js.map +1 -0
- package/dist/services/providers.d.ts +76 -0
- package/dist/services/providers.js +104 -0
- package/dist/services/providers.js.map +1 -0
- package/dist/services/resolve.d.ts +3 -0
- package/dist/services/resolve.js +18 -0
- package/dist/services/resolve.js.map +1 -0
- package/dist/services/resources.d.ts +17 -0
- package/dist/services/resources.js +113 -0
- package/dist/services/resources.js.map +1 -0
- package/dist/services/search.d.ts +29 -0
- package/dist/services/search.js +135 -0
- package/dist/services/search.js.map +1 -0
- package/dist/services/semantic-gate.d.ts +13 -0
- package/dist/services/semantic-gate.js +23 -0
- package/dist/services/semantic-gate.js.map +1 -0
- package/dist/services/semantic.d.ts +113 -0
- package/dist/services/semantic.js +216 -0
- package/dist/services/semantic.js.map +1 -0
- package/dist/services/sensors.d.ts +92 -0
- package/dist/services/sensors.js +246 -0
- package/dist/services/sensors.js.map +1 -0
- package/dist/services/summarize.d.ts +84 -0
- package/dist/services/summarize.js +230 -0
- package/dist/services/summarize.js.map +1 -0
- package/dist/services/tmux-gateway.d.ts +17 -0
- package/dist/services/tmux-gateway.js +10 -0
- package/dist/services/tmux-gateway.js.map +1 -0
- package/dist/services/tmux-resolve.d.ts +54 -0
- package/dist/services/tmux-resolve.js +0 -0
- package/dist/services/tmux-resolve.js.map +1 -0
- package/dist/services/tmux.d.ts +32 -0
- package/dist/services/tmux.js +107 -0
- package/dist/services/tmux.js.map +1 -0
- package/dist/services/views.d.ts +7 -0
- package/dist/services/views.js +18 -0
- package/dist/services/views.js.map +1 -0
- package/dist/services/watch.d.ts +36 -0
- package/dist/services/watch.js +98 -0
- package/dist/services/watch.js.map +1 -0
- package/dist/services/workspace.d.ts +19 -0
- package/dist/services/workspace.js +88 -0
- package/dist/services/workspace.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ask thread, on disk.
|
|
3
|
+
*
|
|
4
|
+
* `gmux ask`'s REPL keeps its turns in a closure and replays them, because the
|
|
5
|
+
* providers we call are one-shot. The picker's chat cannot: every question is
|
|
6
|
+
* answered by a fresh detached worker that exits when it is done, so there is no
|
|
7
|
+
* closure left to hold anything. **The transcript file IS the turn array**, and
|
|
8
|
+
* `foldCompletedTurns` is what turns it back into one — `buildAskPrompt` never
|
|
9
|
+
* learns the difference.
|
|
10
|
+
*
|
|
11
|
+
* An append-only JSONL *event* log, not a document, and three things force that:
|
|
12
|
+
*
|
|
13
|
+
* 1. **A question must land before its answer exists.** The echo of what you
|
|
14
|
+
* typed appears the instant you press enter, ~9–20s before there is an answer
|
|
15
|
+
* to pair it with. A turn record cannot be written until both halves exist.
|
|
16
|
+
* 2. **Two writers.** `__ask-send` appends `meta`/`question`, the worker appends
|
|
17
|
+
* `chunk`/`end`. `O_APPEND` makes the offset update atomic, so ordering holds
|
|
18
|
+
* for whole records — and for nothing else.
|
|
19
|
+
* 3. **The reader is unsynchronized.** The preview re-runs on every cursor move
|
|
20
|
+
* and takes no lock, so it must be able to parse a file that is being
|
|
21
|
+
* appended to right now.
|
|
22
|
+
*
|
|
23
|
+
* Which is why **gmux must never truncate-and-rewrite a transcript**: a reader
|
|
24
|
+
* would catch the file mid-rewrite and the pane would flash empty, or show the
|
|
25
|
+
* previous answer. Appends are whole-line and ordered, so the only line a reader
|
|
26
|
+
* can ever see torn is the last one — `parseTranscript` drops it and the next
|
|
27
|
+
* refresh gets it. A dropped tail costs one repaint; a lock on the read path
|
|
28
|
+
* would cost the whole format.
|
|
29
|
+
*/
|
|
30
|
+
import { type SpawnOptions } from "node:child_process";
|
|
31
|
+
import type { AskEvent, AskTurn } from "../core/types.js";
|
|
32
|
+
import { type AutoSummarizeLock } from "./auto-summarize.js";
|
|
33
|
+
/**
|
|
34
|
+
* How many prior turns a worker replays into the prompt.
|
|
35
|
+
*
|
|
36
|
+
* Every turn re-sends every earlier answer, so tokens grow quadratically and the
|
|
37
|
+
* 300s timeout starts to bite exactly when the thread gets useful. A bare slice
|
|
38
|
+
* of the most recent turns is the bound.
|
|
39
|
+
*
|
|
40
|
+
* Bounded at the fold, deliberately: `buildAskPrompt` is shared with bare
|
|
41
|
+
* `gmux ask`, which this design does not change.
|
|
42
|
+
*/
|
|
43
|
+
export declare const ASK_MAX_REPLAY_TURNS = 8;
|
|
44
|
+
/**
|
|
45
|
+
* The in-flight heartbeat. The only timer in the design.
|
|
46
|
+
*
|
|
47
|
+
* The provider buffers — a 40-line request came back as one 111-byte write at
|
|
48
|
+
* +3.32s — so nothing else fires for the whole think, and a pane that is silent
|
|
49
|
+
* and static for 20s is indistinguishable from a wedged one. This is what makes
|
|
50
|
+
* `thinking… 14s` tick. It exists only while a request is in flight; that is the
|
|
51
|
+
* whole of the exception to "no idle polling".
|
|
52
|
+
*/
|
|
53
|
+
export declare const ASK_HEARTBEAT_MS = 1000;
|
|
54
|
+
/**
|
|
55
|
+
* Trailing-edge throttle on chunk-driven refreshes.
|
|
56
|
+
*
|
|
57
|
+
* Against `claude -p` this fires once, with the answer. It is throttled anyway
|
|
58
|
+
* because `onChunk` is a tee: a provider that trickles would otherwise spawn one
|
|
59
|
+
* preview process per chunk. fzf does not save us — measured on 0.74, 100
|
|
60
|
+
* back-to-back `refresh-preview` POSTs ran a fast preview command 100 times. It
|
|
61
|
+
* bounds concurrency, not spawn rate.
|
|
62
|
+
*/
|
|
63
|
+
export declare const REFRESH_INTERVAL_MS = 150;
|
|
64
|
+
/** How long a cancelled worker's group gets to die politely before SIGKILL. */
|
|
65
|
+
export declare const ASK_KILL_GRACE_MS = 250;
|
|
66
|
+
/**
|
|
67
|
+
* A parsed transcript. `torn` is true when the final line failed to parse —
|
|
68
|
+
* normal, not an error: the writer is appending as we read.
|
|
69
|
+
*/
|
|
70
|
+
export interface AskTranscript {
|
|
71
|
+
events: AskEvent[];
|
|
72
|
+
torn: boolean;
|
|
73
|
+
}
|
|
74
|
+
/** Same shape and same staleness predicate as the auto-summarize lock. */
|
|
75
|
+
export type AskLock = AutoSummarizeLock;
|
|
76
|
+
/** `<pid>-<rand8>`: the pid reaps, the random half keeps two pickers apart. */
|
|
77
|
+
export declare function newAskRunId(pid?: number): string;
|
|
78
|
+
/** The transcript for a fresh run. Allocates a path and creates nothing. */
|
|
79
|
+
export declare function newAskTranscriptPath(): string;
|
|
80
|
+
/** The run id a transcript path carries. Derived, so it is never stored twice. */
|
|
81
|
+
export declare function askRunIdOf(transcript: string): string;
|
|
82
|
+
/**
|
|
83
|
+
* Open a transcript for appending, creating it and its directory if needed.
|
|
84
|
+
*
|
|
85
|
+
* The fd is the caller's to close, and it is held for the life of the turn:
|
|
86
|
+
* `openSync`/`writeSync` rather than a stream because `createWriteStream` can
|
|
87
|
+
* defer its flush past the refresh POST, and with one paint per answer that is
|
|
88
|
+
* not a frame of lag — it is the whole answer missing until something else
|
|
89
|
+
* happens to repaint. **The bytes are on disk before the POST goes out.**
|
|
90
|
+
*/
|
|
91
|
+
export declare function openAskTranscript(transcript: string): number;
|
|
92
|
+
/** One record, one whole-line append. Synchronous — see `openAskTranscript`. */
|
|
93
|
+
export declare function appendAskEvent(fd: number, event: AskEvent): void;
|
|
94
|
+
/**
|
|
95
|
+
* Parse a transcript's text.
|
|
96
|
+
*
|
|
97
|
+
* Takes no lock and cannot fail. A half-written last line is the normal case,
|
|
98
|
+
* not the error case — the reader is a preview re-running on a keystroke while
|
|
99
|
+
* a worker appends a multi-KB answer. Whole-line appends mean only the last line
|
|
100
|
+
* can ever be torn, so everything before it is still true.
|
|
101
|
+
*/
|
|
102
|
+
export declare function parseTranscript(text: string): AskTranscript;
|
|
103
|
+
/**
|
|
104
|
+
* Read a transcript. A missing file is the empty state, not an error — it is
|
|
105
|
+
* how "no conversation yet" costs no flag and no branch.
|
|
106
|
+
*/
|
|
107
|
+
export declare function readAskTranscript(transcript: string): Promise<AskTranscript>;
|
|
108
|
+
/** The seq the next question takes. Questions are the only thing that allocates one. */
|
|
109
|
+
export declare function nextSeq(events: readonly AskEvent[]): number;
|
|
110
|
+
/**
|
|
111
|
+
* The turn being answered right now, if any — a question with no `end`,
|
|
112
|
+
* `aborted` or `error` against it. What `__ask-cancel` aborts, and what the pane
|
|
113
|
+
* renders as `thinking… Ns`.
|
|
114
|
+
*/
|
|
115
|
+
export declare function inFlightSeq(events: readonly AskEvent[]): number | null;
|
|
116
|
+
/**
|
|
117
|
+
* The conversation so far, as `buildAskPrompt` wants it.
|
|
118
|
+
*
|
|
119
|
+
* Two rules carry the weight:
|
|
120
|
+
*
|
|
121
|
+
* - **Keyed by `seq`, never file order.** Chunks are contiguous in practice
|
|
122
|
+
* because the lock guarantees one writer, but folding by position would make
|
|
123
|
+
* this function's correctness depend on a lock in another module.
|
|
124
|
+
* - **A turn enters history only with an `end` record.** In-flight, aborted and
|
|
125
|
+
* errored turns are excluded, so an aborted question is dropped from the
|
|
126
|
+
* model's view entirely (the human still sees it in the pane). A half-written
|
|
127
|
+
* answer promoted to history is shown to the model as its own completed
|
|
128
|
+
* statement of fact, and it will build on a sentence that stops mid-clause.
|
|
129
|
+
* "The answer so far" and "the conversation so far" are different things.
|
|
130
|
+
*/
|
|
131
|
+
export declare function foldCompletedTurns(events: readonly AskEvent[], options?: {
|
|
132
|
+
maxTurns?: number;
|
|
133
|
+
}): AskTurn[];
|
|
134
|
+
/**
|
|
135
|
+
* `transform` returns immediately, so nothing stops you pressing enter twice.
|
|
136
|
+
*
|
|
137
|
+
* Two workers interleaving chunks of different seqs is the one thing that makes
|
|
138
|
+
* the fold ambiguous. With the lock the second enter appends nothing and
|
|
139
|
+
* refreshes a pane that already says "still answering". A key that visibly does
|
|
140
|
+
* nothing for a stated reason is fine; a key that silently corrupts the thread
|
|
141
|
+
* is not.
|
|
142
|
+
*/
|
|
143
|
+
export declare function askLockPath(transcript: string): string;
|
|
144
|
+
/**
|
|
145
|
+
* Where ctrl-o parks the browse query while ask mode owns fzf's query line.
|
|
146
|
+
*
|
|
147
|
+
* A sibling of the transcript rather than a state directory: it inherits the
|
|
148
|
+
* `<pid>-<rand8>` uniqueness for free, so two concurrent pickers cannot collide,
|
|
149
|
+
* and it falls into the cleanup and sweep paths that already exist with no new
|
|
150
|
+
* concept to name.
|
|
151
|
+
*
|
|
152
|
+
* The picker spells this suffix a second time, because it may not import a
|
|
153
|
+
* service and the path is baked into a shell binding. A drift between the two is
|
|
154
|
+
* SILENT — esc restores an empty query and the browse filter is gone — so
|
|
155
|
+
* tests/picker.test.ts pins them equal.
|
|
156
|
+
*/
|
|
157
|
+
export declare function askBrowseQueryPath(transcript: string): string;
|
|
158
|
+
export declare function readAskLock(transcript: string): Promise<AskLock | null>;
|
|
159
|
+
/**
|
|
160
|
+
* Take the turn lock, or report that a turn is already in flight.
|
|
161
|
+
*
|
|
162
|
+
* `pid: 0` until the worker exists — the same acquired-but-not-yet-spawned shape
|
|
163
|
+
* `auto-summarize` uses, and the reason `isLockStale` ANDs liveness with age
|
|
164
|
+
* rather than trusting a pid on its own. That AND is also the pid-reuse guard:
|
|
165
|
+
* a recycled pid can only look alive, never young.
|
|
166
|
+
*/
|
|
167
|
+
export declare function acquireAskLock(transcript: string, now?: Date): Promise<boolean>;
|
|
168
|
+
/** Record the worker that now owns the turn. `__ask-cancel` kills this pid's group. */
|
|
169
|
+
export declare function writeAskLockPid(transcript: string, pid: number, now?: Date): Promise<void>;
|
|
170
|
+
export declare function releaseAskLock(transcript: string): Promise<void>;
|
|
171
|
+
/**
|
|
172
|
+
* Reap the files left behind by pickers that died without cleaning up.
|
|
173
|
+
*
|
|
174
|
+
* Normal exit is `pick.ts`'s `onClose`; this is the `kill -9` path. **A run
|
|
175
|
+
* whose owning pid is alive is never removed** — that is another picker's live
|
|
176
|
+
* thread, and deleting it would empty a pane someone is reading.
|
|
177
|
+
*
|
|
178
|
+
* The spec also says "or the mtime is older than LOCK_STALE_MS", on the grounds
|
|
179
|
+
* that a live worker's file cannot be 10 minutes idle. That reasoning does not
|
|
180
|
+
* survive the filename: the pid is the *picker's*, not the worker's, and a
|
|
181
|
+
* picker left open for an hour after one question is entirely ordinary. So age
|
|
182
|
+
* alone reaps only a run we cannot attribute to a pid at all.
|
|
183
|
+
*
|
|
184
|
+
* Never throws, and never able to fail the picker: an orphan is a few KB of
|
|
185
|
+
* disposable cache, so the cost of not reaping is nil and the cost of a sweep
|
|
186
|
+
* that throws is a picker that will not open.
|
|
187
|
+
*/
|
|
188
|
+
export declare function sweepAskTranscripts(now?: Date, staleMs?: number): Promise<string[]>;
|
|
189
|
+
/**
|
|
190
|
+
* How `__ask-send` forks the worker. Pure, so the decision is testable without
|
|
191
|
+
* spawning anything — the same seam `maybeAutoSummarize`'s `spawnWorker` has.
|
|
192
|
+
*
|
|
193
|
+
* `detached` is not a nicety twice over: it lets the sender exit at once, and it
|
|
194
|
+
* makes the worker a process-group leader, which is the only thing that lets
|
|
195
|
+
* `__ask-cancel` reap the provider underneath it. `spawn` does not kill its
|
|
196
|
+
* child on POSIX, so a worker that merely exits leaves `claude -p` running and
|
|
197
|
+
* billing. (`setsid` does not exist on macOS — gmux's own platform — so Node's
|
|
198
|
+
* `detached: true` is the portable equivalent; it calls `setsid(2)`.)
|
|
199
|
+
*
|
|
200
|
+
* `childEnv()`, not `process.env`: the worker is a `gmux` that is about to make a
|
|
201
|
+
* model call, which is the definition of a child — its provider may shell back
|
|
202
|
+
* into `gmux grep`, and that nested `gmux` must not start a summarize pass. The fzf
|
|
203
|
+
* api key rides here by inheritance and nowhere else; it must never reach an
|
|
204
|
+
* argv, where `ps` would hand it to any user on the box.
|
|
205
|
+
*/
|
|
206
|
+
export declare function askChildSpawnOptions(env?: NodeJS.ProcessEnv): SpawnOptions;
|
|
207
|
+
/**
|
|
208
|
+
* Re-enter this same CLI, detached, on a hidden command.
|
|
209
|
+
*
|
|
210
|
+
* `execArgv` is forwarded for the reason picker.ts already documents once: under
|
|
211
|
+
* `npm run dev` the entry is `src/cli/main.ts` and `execArgv` carries tsx's
|
|
212
|
+
* loader flags. Drop them and the command is `node src/cli/main.ts`, which Node
|
|
213
|
+
* 20 cannot run — so in development every answer silently never arrives, with
|
|
214
|
+
* `stdio: "ignore"` swallowing the evidence.
|
|
215
|
+
*/
|
|
216
|
+
export declare function spawnAskWorker(args: readonly string[]): number | undefined;
|
|
217
|
+
/** Throttle state. `lastAt` is null before the first refresh; `final` bypasses the interval. */
|
|
218
|
+
export interface RefreshState {
|
|
219
|
+
lastAt: number | null;
|
|
220
|
+
pending: boolean;
|
|
221
|
+
final: boolean;
|
|
222
|
+
}
|
|
223
|
+
/** The throttle policy, as a function of state, so it is testable without a socket. */
|
|
224
|
+
export declare function shouldRefresh(state: RefreshState, now: number): boolean;
|
|
225
|
+
export interface StreamAnswerOptions {
|
|
226
|
+
/** The provider's argv. The fake-binary seam: tests pass a `node -e` here. */
|
|
227
|
+
argv: readonly string[];
|
|
228
|
+
prompt: string;
|
|
229
|
+
/** Open for append, owned by the caller, closed by the caller. */
|
|
230
|
+
transcriptFd: number;
|
|
231
|
+
seq: number;
|
|
232
|
+
/** Injected, so the write-then-notify order is testable without a port. */
|
|
233
|
+
notify: () => void;
|
|
234
|
+
signal: AbortSignal;
|
|
235
|
+
timeoutMs?: number;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Run one turn: call the provider, append what it says, tell fzf to repaint.
|
|
239
|
+
*
|
|
240
|
+
* **Write, then notify.** Notify-then-write renders the previous chunk, forever
|
|
241
|
+
* one behind — and with a provider that buffers, "one behind" means an empty
|
|
242
|
+
* pane until something else repaints it.
|
|
243
|
+
*
|
|
244
|
+
* **No `env` is passed to `runProviderCommand`, and that is load-bearing.** It
|
|
245
|
+
* defaults to `childEnv()`, which is what stops the provider's `gmux grep` from
|
|
246
|
+
* starting a summarize pass. A well-meant `env: { ...process.env, FZF_PORT }`
|
|
247
|
+
* here would silently drop `GMUX_CHILD` and reopen the loop — which is why
|
|
248
|
+
* the port travels on the worker's argv instead, where the trap cannot be set.
|
|
249
|
+
*
|
|
250
|
+
* **The canceller owns the `aborted` record, not us.** On abort we write nothing
|
|
251
|
+
* and return: a wedged worker must not be able to leave the pane stuck on
|
|
252
|
+
* "answering", and only the process that knows the kill happened can be sure.
|
|
253
|
+
*/
|
|
254
|
+
export declare function streamAnswer(options: StreamAnswerOptions): Promise<void>;
|
|
255
|
+
/** Close a transcript fd. Always from a `finally` — the fd lives for the turn. */
|
|
256
|
+
export declare function closeAskTranscript(fd: number): void;
|