@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,544 @@
|
|
|
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 { spawn } from "node:child_process";
|
|
31
|
+
import { randomBytes } from "node:crypto";
|
|
32
|
+
import { closeSync, mkdirSync, openSync, writeSync } from "node:fs";
|
|
33
|
+
import { open, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
34
|
+
import { basename, dirname } from "node:path";
|
|
35
|
+
import { AskProviderError } from "../core/errors.js";
|
|
36
|
+
import { askTranscriptDir, askTranscriptPath } from "../core/paths.js";
|
|
37
|
+
import { ASK_TIMEOUT_MS } from "./ask.js";
|
|
38
|
+
import { isLockStale, LOCK_STALE_MS } from "./auto-summarize.js";
|
|
39
|
+
import { childEnv } from "./config.js";
|
|
40
|
+
import { runProviderCommand } from "./provider-process.js";
|
|
41
|
+
/**
|
|
42
|
+
* How many prior turns a worker replays into the prompt.
|
|
43
|
+
*
|
|
44
|
+
* Every turn re-sends every earlier answer, so tokens grow quadratically and the
|
|
45
|
+
* 300s timeout starts to bite exactly when the thread gets useful. A bare slice
|
|
46
|
+
* of the most recent turns is the bound.
|
|
47
|
+
*
|
|
48
|
+
* Bounded at the fold, deliberately: `buildAskPrompt` is shared with bare
|
|
49
|
+
* `gmux ask`, which this design does not change.
|
|
50
|
+
*/
|
|
51
|
+
export const ASK_MAX_REPLAY_TURNS = 8;
|
|
52
|
+
/**
|
|
53
|
+
* The in-flight heartbeat. The only timer in the design.
|
|
54
|
+
*
|
|
55
|
+
* The provider buffers — a 40-line request came back as one 111-byte write at
|
|
56
|
+
* +3.32s — so nothing else fires for the whole think, and a pane that is silent
|
|
57
|
+
* and static for 20s is indistinguishable from a wedged one. This is what makes
|
|
58
|
+
* `thinking… 14s` tick. It exists only while a request is in flight; that is the
|
|
59
|
+
* whole of the exception to "no idle polling".
|
|
60
|
+
*/
|
|
61
|
+
export const ASK_HEARTBEAT_MS = 1_000;
|
|
62
|
+
/**
|
|
63
|
+
* Trailing-edge throttle on chunk-driven refreshes.
|
|
64
|
+
*
|
|
65
|
+
* Against `claude -p` this fires once, with the answer. It is throttled anyway
|
|
66
|
+
* because `onChunk` is a tee: a provider that trickles would otherwise spawn one
|
|
67
|
+
* preview process per chunk. fzf does not save us — measured on 0.74, 100
|
|
68
|
+
* back-to-back `refresh-preview` POSTs ran a fast preview command 100 times. It
|
|
69
|
+
* bounds concurrency, not spawn rate.
|
|
70
|
+
*/
|
|
71
|
+
export const REFRESH_INTERVAL_MS = 150;
|
|
72
|
+
/** How long a cancelled worker's group gets to die politely before SIGKILL. */
|
|
73
|
+
export const ASK_KILL_GRACE_MS = 250;
|
|
74
|
+
/** `<pid>-<rand8>`: the pid reaps, the random half keeps two pickers apart. */
|
|
75
|
+
export function newAskRunId(pid = process.pid) {
|
|
76
|
+
return `${pid}-${randomBytes(4).toString("hex")}`;
|
|
77
|
+
}
|
|
78
|
+
/** The transcript for a fresh run. Allocates a path and creates nothing. */
|
|
79
|
+
export function newAskTranscriptPath() {
|
|
80
|
+
return askTranscriptPath(newAskRunId());
|
|
81
|
+
}
|
|
82
|
+
/** The run id a transcript path carries. Derived, so it is never stored twice. */
|
|
83
|
+
export function askRunIdOf(transcript) {
|
|
84
|
+
return basename(transcript, ".jsonl");
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Open a transcript for appending, creating it and its directory if needed.
|
|
88
|
+
*
|
|
89
|
+
* The fd is the caller's to close, and it is held for the life of the turn:
|
|
90
|
+
* `openSync`/`writeSync` rather than a stream because `createWriteStream` can
|
|
91
|
+
* defer its flush past the refresh POST, and with one paint per answer that is
|
|
92
|
+
* not a frame of lag — it is the whole answer missing until something else
|
|
93
|
+
* happens to repaint. **The bytes are on disk before the POST goes out.**
|
|
94
|
+
*/
|
|
95
|
+
export function openAskTranscript(transcript) {
|
|
96
|
+
mkdirSync(dirname(transcript), { recursive: true });
|
|
97
|
+
return openSync(transcript, "a");
|
|
98
|
+
}
|
|
99
|
+
/** One record, one whole-line append. Synchronous — see `openAskTranscript`. */
|
|
100
|
+
export function appendAskEvent(fd, event) {
|
|
101
|
+
writeSync(fd, `${JSON.stringify(event)}\n`);
|
|
102
|
+
}
|
|
103
|
+
function isAskEvent(value) {
|
|
104
|
+
if (!value || typeof value !== "object")
|
|
105
|
+
return false;
|
|
106
|
+
const t = value.t;
|
|
107
|
+
return t === "meta" || t === "question" || t === "chunk" || t === "end" || t === "aborted" || t === "error";
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Parse a transcript's text.
|
|
111
|
+
*
|
|
112
|
+
* Takes no lock and cannot fail. A half-written last line is the normal case,
|
|
113
|
+
* not the error case — the reader is a preview re-running on a keystroke while
|
|
114
|
+
* a worker appends a multi-KB answer. Whole-line appends mean only the last line
|
|
115
|
+
* can ever be torn, so everything before it is still true.
|
|
116
|
+
*/
|
|
117
|
+
export function parseTranscript(text) {
|
|
118
|
+
const lines = text.split("\n");
|
|
119
|
+
const events = [];
|
|
120
|
+
let torn = false;
|
|
121
|
+
for (const [index, line] of lines.entries()) {
|
|
122
|
+
if (line.trim() === "")
|
|
123
|
+
continue;
|
|
124
|
+
let parsed;
|
|
125
|
+
try {
|
|
126
|
+
parsed = JSON.parse(line);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Only the tail can be torn. A broken line anywhere else is a file we
|
|
130
|
+
// don't understand, and skipping it beats guessing at it.
|
|
131
|
+
if (index === lines.length - 1)
|
|
132
|
+
torn = true;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (isAskEvent(parsed))
|
|
136
|
+
events.push(parsed);
|
|
137
|
+
}
|
|
138
|
+
return { events, torn };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Read a transcript. A missing file is the empty state, not an error — it is
|
|
142
|
+
* how "no conversation yet" costs no flag and no branch.
|
|
143
|
+
*/
|
|
144
|
+
export async function readAskTranscript(transcript) {
|
|
145
|
+
try {
|
|
146
|
+
return parseTranscript(await readFile(transcript, "utf8"));
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return { events: [], torn: false };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** The seq the next question takes. Questions are the only thing that allocates one. */
|
|
153
|
+
export function nextSeq(events) {
|
|
154
|
+
let max = 0;
|
|
155
|
+
for (const event of events) {
|
|
156
|
+
if (event.t === "question" && event.seq > max)
|
|
157
|
+
max = event.seq;
|
|
158
|
+
}
|
|
159
|
+
return max + 1;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* The turn being answered right now, if any — a question with no `end`,
|
|
163
|
+
* `aborted` or `error` against it. What `__ask-cancel` aborts, and what the pane
|
|
164
|
+
* renders as `thinking… Ns`.
|
|
165
|
+
*/
|
|
166
|
+
export function inFlightSeq(events) {
|
|
167
|
+
const settled = new Set();
|
|
168
|
+
let latest = null;
|
|
169
|
+
for (const event of events) {
|
|
170
|
+
if (event.t === "question")
|
|
171
|
+
latest = latest === null ? event.seq : Math.max(latest, event.seq);
|
|
172
|
+
if (event.t === "end" || event.t === "aborted" || event.t === "error")
|
|
173
|
+
settled.add(event.seq);
|
|
174
|
+
}
|
|
175
|
+
return latest !== null && !settled.has(latest) ? latest : null;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The conversation so far, as `buildAskPrompt` wants it.
|
|
179
|
+
*
|
|
180
|
+
* Two rules carry the weight:
|
|
181
|
+
*
|
|
182
|
+
* - **Keyed by `seq`, never file order.** Chunks are contiguous in practice
|
|
183
|
+
* because the lock guarantees one writer, but folding by position would make
|
|
184
|
+
* this function's correctness depend on a lock in another module.
|
|
185
|
+
* - **A turn enters history only with an `end` record.** In-flight, aborted and
|
|
186
|
+
* errored turns are excluded, so an aborted question is dropped from the
|
|
187
|
+
* model's view entirely (the human still sees it in the pane). A half-written
|
|
188
|
+
* answer promoted to history is shown to the model as its own completed
|
|
189
|
+
* statement of fact, and it will build on a sentence that stops mid-clause.
|
|
190
|
+
* "The answer so far" and "the conversation so far" are different things.
|
|
191
|
+
*/
|
|
192
|
+
export function foldCompletedTurns(events, options = {}) {
|
|
193
|
+
const questions = new Map();
|
|
194
|
+
const chunks = new Map();
|
|
195
|
+
const ended = new Set();
|
|
196
|
+
for (const event of events) {
|
|
197
|
+
switch (event.t) {
|
|
198
|
+
case "question":
|
|
199
|
+
questions.set(event.seq, event.text);
|
|
200
|
+
break;
|
|
201
|
+
case "chunk": {
|
|
202
|
+
const existing = chunks.get(event.seq);
|
|
203
|
+
if (existing)
|
|
204
|
+
existing.push(event.text);
|
|
205
|
+
else
|
|
206
|
+
chunks.set(event.seq, [event.text]);
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
case "end":
|
|
210
|
+
ended.add(event.seq);
|
|
211
|
+
break;
|
|
212
|
+
default:
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const turns = [];
|
|
217
|
+
for (const seq of [...ended].sort((a, b) => a - b)) {
|
|
218
|
+
const question = questions.get(seq);
|
|
219
|
+
const answer = (chunks.get(seq) ?? []).join("").trim();
|
|
220
|
+
if (question === undefined || answer === "")
|
|
221
|
+
continue;
|
|
222
|
+
turns.push({ question, answer });
|
|
223
|
+
}
|
|
224
|
+
const maxTurns = options.maxTurns ?? ASK_MAX_REPLAY_TURNS;
|
|
225
|
+
return maxTurns >= 0 && turns.length > maxTurns ? turns.slice(-maxTurns) : turns;
|
|
226
|
+
}
|
|
227
|
+
/* ---------------------------------------------------------------- the lock */
|
|
228
|
+
/**
|
|
229
|
+
* `transform` returns immediately, so nothing stops you pressing enter twice.
|
|
230
|
+
*
|
|
231
|
+
* Two workers interleaving chunks of different seqs is the one thing that makes
|
|
232
|
+
* the fold ambiguous. With the lock the second enter appends nothing and
|
|
233
|
+
* refreshes a pane that already says "still answering". A key that visibly does
|
|
234
|
+
* nothing for a stated reason is fine; a key that silently corrupts the thread
|
|
235
|
+
* is not.
|
|
236
|
+
*/
|
|
237
|
+
export function askLockPath(transcript) {
|
|
238
|
+
return `${transcript}.lock`;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Where ctrl-o parks the browse query while ask mode owns fzf's query line.
|
|
242
|
+
*
|
|
243
|
+
* A sibling of the transcript rather than a state directory: it inherits the
|
|
244
|
+
* `<pid>-<rand8>` uniqueness for free, so two concurrent pickers cannot collide,
|
|
245
|
+
* and it falls into the cleanup and sweep paths that already exist with no new
|
|
246
|
+
* concept to name.
|
|
247
|
+
*
|
|
248
|
+
* The picker spells this suffix a second time, because it may not import a
|
|
249
|
+
* service and the path is baked into a shell binding. A drift between the two is
|
|
250
|
+
* SILENT — esc restores an empty query and the browse filter is gone — so
|
|
251
|
+
* tests/picker.test.ts pins them equal.
|
|
252
|
+
*/
|
|
253
|
+
export function askBrowseQueryPath(transcript) {
|
|
254
|
+
return `${transcript}.browseq`;
|
|
255
|
+
}
|
|
256
|
+
export async function readAskLock(transcript) {
|
|
257
|
+
try {
|
|
258
|
+
const parsed = JSON.parse(await readFile(askLockPath(transcript), "utf8"));
|
|
259
|
+
if (typeof parsed?.startedAt !== "string")
|
|
260
|
+
return null;
|
|
261
|
+
return { pid: Number(parsed.pid) || 0, startedAt: parsed.startedAt };
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return null; // No lock, or an unreadable one: treat as not held.
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async function createAskLockExclusive(transcript, lock) {
|
|
268
|
+
let handle;
|
|
269
|
+
try {
|
|
270
|
+
handle = await open(askLockPath(transcript), "wx");
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return false; // EEXIST: the other enter got there first.
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
await handle.writeFile(JSON.stringify(lock), "utf8");
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
await handle.close();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Take the turn lock, or report that a turn is already in flight.
|
|
285
|
+
*
|
|
286
|
+
* `pid: 0` until the worker exists — the same acquired-but-not-yet-spawned shape
|
|
287
|
+
* `auto-summarize` uses, and the reason `isLockStale` ANDs liveness with age
|
|
288
|
+
* rather than trusting a pid on its own. That AND is also the pid-reuse guard:
|
|
289
|
+
* a recycled pid can only look alive, never young.
|
|
290
|
+
*/
|
|
291
|
+
export async function acquireAskLock(transcript, now = new Date()) {
|
|
292
|
+
mkdirSync(dirname(transcript), { recursive: true });
|
|
293
|
+
const lock = { pid: 0, startedAt: now.toISOString() };
|
|
294
|
+
if (await createAskLockExclusive(transcript, lock))
|
|
295
|
+
return true;
|
|
296
|
+
const existing = await readAskLock(transcript);
|
|
297
|
+
if (existing && !isLockStale(existing, now))
|
|
298
|
+
return false;
|
|
299
|
+
await rm(askLockPath(transcript), { force: true });
|
|
300
|
+
return createAskLockExclusive(transcript, lock);
|
|
301
|
+
}
|
|
302
|
+
/** Record the worker that now owns the turn. `__ask-cancel` kills this pid's group. */
|
|
303
|
+
export async function writeAskLockPid(transcript, pid, now = new Date()) {
|
|
304
|
+
await writeFile(askLockPath(transcript), JSON.stringify({ pid, startedAt: now.toISOString() }), "utf8");
|
|
305
|
+
}
|
|
306
|
+
export async function releaseAskLock(transcript) {
|
|
307
|
+
await rm(askLockPath(transcript), { force: true });
|
|
308
|
+
}
|
|
309
|
+
/* --------------------------------------------------------------- the sweep */
|
|
310
|
+
function processAlive(pid) {
|
|
311
|
+
if (pid <= 0)
|
|
312
|
+
return false;
|
|
313
|
+
try {
|
|
314
|
+
process.kill(pid, 0);
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
// EPERM means the process exists but belongs to someone else — still alive.
|
|
319
|
+
return error.code === "EPERM";
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Every run with a file in the directory, however far its picker got.
|
|
324
|
+
*
|
|
325
|
+
* Keyed on the RUN, not on the transcript: `ctrl-o` writes `.browseq` before a
|
|
326
|
+
* question exists, so a picker killed between `ctrl-o` and `enter` leaves a
|
|
327
|
+
* `.browseq` with no `.jsonl` beside it. Keying the sweep on `.jsonl` — the file
|
|
328
|
+
* that happens to be reaped first — made that one unreapable forever, and a
|
|
329
|
+
* cache that only ever grows is the bug the sweep exists to prevent.
|
|
330
|
+
*/
|
|
331
|
+
function askRunIds(entries) {
|
|
332
|
+
const ids = new Set();
|
|
333
|
+
for (const entry of entries) {
|
|
334
|
+
const base = entry.replace(/\.(browseq|lock)$/, "");
|
|
335
|
+
if (base.endsWith(".jsonl"))
|
|
336
|
+
ids.add(basename(base, ".jsonl"));
|
|
337
|
+
}
|
|
338
|
+
return [...ids];
|
|
339
|
+
}
|
|
340
|
+
/** The newest mtime among a run's files, or null when none of them exist. */
|
|
341
|
+
async function newestMtime(paths) {
|
|
342
|
+
let newest = null;
|
|
343
|
+
for (const path of paths) {
|
|
344
|
+
try {
|
|
345
|
+
const at = (await stat(path)).mtime.getTime();
|
|
346
|
+
if (newest === null || at > newest)
|
|
347
|
+
newest = at;
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
// Not every member of a run exists — that is the normal case, not an error.
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return newest;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Reap the files left behind by pickers that died without cleaning up.
|
|
357
|
+
*
|
|
358
|
+
* Normal exit is `pick.ts`'s `onClose`; this is the `kill -9` path. **A run
|
|
359
|
+
* whose owning pid is alive is never removed** — that is another picker's live
|
|
360
|
+
* thread, and deleting it would empty a pane someone is reading.
|
|
361
|
+
*
|
|
362
|
+
* The spec also says "or the mtime is older than LOCK_STALE_MS", on the grounds
|
|
363
|
+
* that a live worker's file cannot be 10 minutes idle. That reasoning does not
|
|
364
|
+
* survive the filename: the pid is the *picker's*, not the worker's, and a
|
|
365
|
+
* picker left open for an hour after one question is entirely ordinary. So age
|
|
366
|
+
* alone reaps only a run we cannot attribute to a pid at all.
|
|
367
|
+
*
|
|
368
|
+
* Never throws, and never able to fail the picker: an orphan is a few KB of
|
|
369
|
+
* disposable cache, so the cost of not reaping is nil and the cost of a sweep
|
|
370
|
+
* that throws is a picker that will not open.
|
|
371
|
+
*/
|
|
372
|
+
export async function sweepAskTranscripts(now = new Date(), staleMs = LOCK_STALE_MS) {
|
|
373
|
+
const removed = [];
|
|
374
|
+
let entries;
|
|
375
|
+
try {
|
|
376
|
+
entries = await readdir(askTranscriptDir());
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
return removed; // No directory: nobody has ever asked. Nothing to sweep.
|
|
380
|
+
}
|
|
381
|
+
for (const runId of askRunIds(entries)) {
|
|
382
|
+
const transcript = askTranscriptPath(runId);
|
|
383
|
+
const members = [transcript, askLockPath(transcript), askBrowseQueryPath(transcript)];
|
|
384
|
+
try {
|
|
385
|
+
const pid = Number.parseInt(runId.split("-")[0] ?? "", 10);
|
|
386
|
+
if (Number.isFinite(pid) && processAlive(pid))
|
|
387
|
+
continue;
|
|
388
|
+
if (!Number.isFinite(pid)) {
|
|
389
|
+
const mtime = await newestMtime(members);
|
|
390
|
+
if (mtime === null || now.getTime() - mtime <= staleMs)
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
for (const member of members)
|
|
394
|
+
await rm(member, { force: true });
|
|
395
|
+
removed.push(transcript);
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
// One unreadable entry must not cost the others their sweep.
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return removed;
|
|
402
|
+
}
|
|
403
|
+
/* --------------------------------------------------------- the worker spawn */
|
|
404
|
+
/**
|
|
405
|
+
* How `__ask-send` forks the worker. Pure, so the decision is testable without
|
|
406
|
+
* spawning anything — the same seam `maybeAutoSummarize`'s `spawnWorker` has.
|
|
407
|
+
*
|
|
408
|
+
* `detached` is not a nicety twice over: it lets the sender exit at once, and it
|
|
409
|
+
* makes the worker a process-group leader, which is the only thing that lets
|
|
410
|
+
* `__ask-cancel` reap the provider underneath it. `spawn` does not kill its
|
|
411
|
+
* child on POSIX, so a worker that merely exits leaves `claude -p` running and
|
|
412
|
+
* billing. (`setsid` does not exist on macOS — gmux's own platform — so Node's
|
|
413
|
+
* `detached: true` is the portable equivalent; it calls `setsid(2)`.)
|
|
414
|
+
*
|
|
415
|
+
* `childEnv()`, not `process.env`: the worker is a `gmux` that is about to make a
|
|
416
|
+
* model call, which is the definition of a child — its provider may shell back
|
|
417
|
+
* into `gmux grep`, and that nested `gmux` must not start a summarize pass. The fzf
|
|
418
|
+
* api key rides here by inheritance and nowhere else; it must never reach an
|
|
419
|
+
* argv, where `ps` would hand it to any user on the box.
|
|
420
|
+
*/
|
|
421
|
+
export function askChildSpawnOptions(env = process.env) {
|
|
422
|
+
return {
|
|
423
|
+
detached: true,
|
|
424
|
+
stdio: "ignore",
|
|
425
|
+
// Belt and braces on top of GMUX_CHILD: the worker must never
|
|
426
|
+
// summarize, or every answer would fork a summarize pass from inside fzf.
|
|
427
|
+
env: { ...childEnv(env), GMUX_AUTO_SUMMARIZE: "0" },
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Re-enter this same CLI, detached, on a hidden command.
|
|
432
|
+
*
|
|
433
|
+
* `execArgv` is forwarded for the reason picker.ts already documents once: under
|
|
434
|
+
* `npm run dev` the entry is `src/cli/main.ts` and `execArgv` carries tsx's
|
|
435
|
+
* loader flags. Drop them and the command is `node src/cli/main.ts`, which Node
|
|
436
|
+
* 20 cannot run — so in development every answer silently never arrives, with
|
|
437
|
+
* `stdio: "ignore"` swallowing the evidence.
|
|
438
|
+
*/
|
|
439
|
+
export function spawnAskWorker(args) {
|
|
440
|
+
const entry = process.argv[1];
|
|
441
|
+
if (!entry)
|
|
442
|
+
return undefined;
|
|
443
|
+
const child = spawn(process.execPath, [...process.execArgv, entry, ...args], askChildSpawnOptions());
|
|
444
|
+
child.unref();
|
|
445
|
+
return child.pid;
|
|
446
|
+
}
|
|
447
|
+
/** The throttle policy, as a function of state, so it is testable without a socket. */
|
|
448
|
+
export function shouldRefresh(state, now) {
|
|
449
|
+
if (state.final)
|
|
450
|
+
return true;
|
|
451
|
+
if (!state.pending)
|
|
452
|
+
return false;
|
|
453
|
+
if (state.lastAt === null)
|
|
454
|
+
return true;
|
|
455
|
+
return now - state.lastAt >= REFRESH_INTERVAL_MS;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Run one turn: call the provider, append what it says, tell fzf to repaint.
|
|
459
|
+
*
|
|
460
|
+
* **Write, then notify.** Notify-then-write renders the previous chunk, forever
|
|
461
|
+
* one behind — and with a provider that buffers, "one behind" means an empty
|
|
462
|
+
* pane until something else repaints it.
|
|
463
|
+
*
|
|
464
|
+
* **No `env` is passed to `runProviderCommand`, and that is load-bearing.** It
|
|
465
|
+
* defaults to `childEnv()`, which is what stops the provider's `gmux grep` from
|
|
466
|
+
* starting a summarize pass. A well-meant `env: { ...process.env, FZF_PORT }`
|
|
467
|
+
* here would silently drop `GMUX_CHILD` and reopen the loop — which is why
|
|
468
|
+
* the port travels on the worker's argv instead, where the trap cannot be set.
|
|
469
|
+
*
|
|
470
|
+
* **The canceller owns the `aborted` record, not us.** On abort we write nothing
|
|
471
|
+
* and return: a wedged worker must not be able to leave the pane stuck on
|
|
472
|
+
* "answering", and only the process that knows the kill happened can be sure.
|
|
473
|
+
*/
|
|
474
|
+
export async function streamAnswer(options) {
|
|
475
|
+
const { transcriptFd: fd, seq, notify, signal } = options;
|
|
476
|
+
const state = { lastAt: null, pending: false, final: false };
|
|
477
|
+
let trailing = null;
|
|
478
|
+
const flush = () => {
|
|
479
|
+
state.pending = false;
|
|
480
|
+
state.lastAt = Date.now();
|
|
481
|
+
notify();
|
|
482
|
+
};
|
|
483
|
+
const onChunk = (text) => {
|
|
484
|
+
appendAskEvent(fd, { t: "chunk", seq, text });
|
|
485
|
+
state.pending = true;
|
|
486
|
+
if (shouldRefresh(state, Date.now())) {
|
|
487
|
+
flush();
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
// Trailing edge: the last chunk of a burst must still land, and the final
|
|
491
|
+
// refresh below covers the case where the burst ends the turn.
|
|
492
|
+
if (!trailing) {
|
|
493
|
+
trailing = setTimeout(() => {
|
|
494
|
+
trailing = null;
|
|
495
|
+
if (state.pending)
|
|
496
|
+
flush();
|
|
497
|
+
}, REFRESH_INTERVAL_MS);
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
try {
|
|
501
|
+
const output = await runProviderCommand(options.argv, options.prompt, {
|
|
502
|
+
timeoutMs: options.timeoutMs ?? ASK_TIMEOUT_MS,
|
|
503
|
+
onChunk,
|
|
504
|
+
signal,
|
|
505
|
+
});
|
|
506
|
+
if (signal.aborted)
|
|
507
|
+
return;
|
|
508
|
+
// The resolved string is every byte `onChunk` was handed, in order, so the
|
|
509
|
+
// chunks on disk are already the whole answer — this only judges it.
|
|
510
|
+
if (output.trim() === "")
|
|
511
|
+
throw new Error("reply was empty");
|
|
512
|
+
appendAskEvent(fd, { t: "end", seq, at: new Date().toISOString() });
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
if (signal.aborted)
|
|
516
|
+
return;
|
|
517
|
+
// fzf owns the terminal, so this error cannot be printed at anyone. It goes
|
|
518
|
+
// where the answer would have gone, carrying its fix — non-negotiable #5
|
|
519
|
+
// does not stop applying because the surface is a pane.
|
|
520
|
+
const failed = new AskProviderError(options.argv.join(" "), error.message);
|
|
521
|
+
appendAskEvent(fd, {
|
|
522
|
+
t: "error",
|
|
523
|
+
seq,
|
|
524
|
+
at: new Date().toISOString(),
|
|
525
|
+
message: failed.fix ? `${failed.message}\n${failed.fix}` : failed.message,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
finally {
|
|
529
|
+
if (trailing)
|
|
530
|
+
clearTimeout(trailing);
|
|
531
|
+
if (!signal.aborted) {
|
|
532
|
+
// One final unthrottled refresh, so the answer cannot be stranded behind
|
|
533
|
+
// the throttle or the cancelled heartbeat.
|
|
534
|
+
state.final = true;
|
|
535
|
+
if (shouldRefresh(state, Date.now()))
|
|
536
|
+
flush();
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
/** Close a transcript fd. Always from a `finally` — the fd lives for the turn. */
|
|
541
|
+
export function closeAskTranscript(fd) {
|
|
542
|
+
closeSync(fd);
|
|
543
|
+
}
|
|
544
|
+
//# sourceMappingURL=ask-transcript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask-transcript.js","sourceRoot":"","sources":["../../src/services/ask-transcript.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask: questions about the sessions you already have, answered from the
|
|
3
|
+
* summaries you already paid for.
|
|
4
|
+
*
|
|
5
|
+
* The design decision worth knowing before you change anything here:
|
|
6
|
+
*
|
|
7
|
+
* **The tool loop is the harness's, not ours.** The prompt tells the model it
|
|
8
|
+
* may run `gmux grep '<query>' --json`, and the provider is invoked with exactly
|
|
9
|
+
* that permission (see providers.ts `askArgv`). We parse no tool calls and speak
|
|
10
|
+
* no vendor protocol — the abstraction stays "a CLI that reads a prompt and
|
|
11
|
+
* writes text", which is the only reason gmux depends on no vendor SDK.
|
|
12
|
+
*
|
|
13
|
+
* The cost is that we don't meter how many greps the model runs. Accepted: grep
|
|
14
|
+
* is read-only and cheap, and the provider's own turn limit bounds it.
|
|
15
|
+
*
|
|
16
|
+
* The context block is bounded the way `distill()` is bounded — a few KB
|
|
17
|
+
* regardless of how many sessions you have — because a REPL re-sends it every
|
|
18
|
+
* turn.
|
|
19
|
+
*/
|
|
20
|
+
import type { AskContext, AskProvider, AskTurn, SessionView } from "../core/types.js";
|
|
21
|
+
/**
|
|
22
|
+
* Ask gets longer than a summary does: the model may run several greps before
|
|
23
|
+
* it answers, and each is a subprocess of its own.
|
|
24
|
+
*/
|
|
25
|
+
export declare const ASK_TIMEOUT_MS = 300000;
|
|
26
|
+
/** How many sessions go into the block. Matches the picker's default window. */
|
|
27
|
+
export declare const ASK_SESSION_LIMIT = 20;
|
|
28
|
+
export declare function buildAskContext(views: readonly SessionView[], focusId?: string | null, limit?: number): AskContext;
|
|
29
|
+
/** Short id: what a human types and what the model should quote back. */
|
|
30
|
+
export declare function shortId(sessionId: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* The prompt for one turn.
|
|
33
|
+
*
|
|
34
|
+
* `turns` is the conversation so far. We re-send it because `claude -p` is
|
|
35
|
+
* one-shot and `--resume` is Claude-specific — carrying state in memory and
|
|
36
|
+
* replaying it is what keeps this working for any provider. Bounded, because
|
|
37
|
+
* the context block is.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildAskPrompt(context: AskContext, turns: readonly AskTurn[], question: string, now?: Date): string;
|
|
40
|
+
export declare class CliAskProvider implements AskProvider {
|
|
41
|
+
readonly name: string;
|
|
42
|
+
private readonly argv;
|
|
43
|
+
constructor(argv: string[]);
|
|
44
|
+
isAvailable(): Promise<boolean>;
|
|
45
|
+
ask(prompt: string): Promise<string>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The ask provider for the current config, or null when the user has said no to
|
|
49
|
+
* model calls.
|
|
50
|
+
*
|
|
51
|
+
* Null is an answer, not a failure — `provider: null` in config means exactly
|
|
52
|
+
* this, and the caller renders it as "run `gmux setup`" rather than as an error.
|
|
53
|
+
*/
|
|
54
|
+
export declare function defaultAskProvider(): Promise<CliAskProvider | null>;
|