@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.
Files changed (229) hide show
  1. package/CHANGELOG.md +654 -0
  2. package/LICENSE +21 -0
  3. package/README.md +351 -0
  4. package/dist/adapters/claude-code.d.ts +52 -0
  5. package/dist/adapters/claude-code.js +315 -0
  6. package/dist/adapters/claude-code.js.map +1 -0
  7. package/dist/adapters/codex.d.ts +39 -0
  8. package/dist/adapters/codex.js +233 -0
  9. package/dist/adapters/codex.js.map +1 -0
  10. package/dist/adapters/jsonl.d.ts +40 -0
  11. package/dist/adapters/jsonl.js +92 -0
  12. package/dist/adapters/jsonl.js.map +1 -0
  13. package/dist/adapters/registry.d.ts +11 -0
  14. package/dist/adapters/registry.js +21 -0
  15. package/dist/adapters/registry.js.map +1 -0
  16. package/dist/adapters/types.d.ts +43 -0
  17. package/dist/adapters/types.js +2 -0
  18. package/dist/adapters/types.js.map +1 -0
  19. package/dist/cli/border-client.d.ts +8 -0
  20. package/dist/cli/border-client.js +11 -0
  21. package/dist/cli/border-client.js.map +1 -0
  22. package/dist/cli/commands/__ask-cancel.d.ts +35 -0
  23. package/dist/cli/commands/__ask-cancel.js +91 -0
  24. package/dist/cli/commands/__ask-cancel.js.map +1 -0
  25. package/dist/cli/commands/__ask-refresh.d.ts +40 -0
  26. package/dist/cli/commands/__ask-refresh.js +54 -0
  27. package/dist/cli/commands/__ask-refresh.js.map +1 -0
  28. package/dist/cli/commands/__ask-run.d.ts +43 -0
  29. package/dist/cli/commands/__ask-run.js +126 -0
  30. package/dist/cli/commands/__ask-run.js.map +1 -0
  31. package/dist/cli/commands/__ask-send.d.ts +50 -0
  32. package/dist/cli/commands/__ask-send.js +138 -0
  33. package/dist/cli/commands/__ask-send.js.map +1 -0
  34. package/dist/cli/commands/__preview-card.d.ts +31 -0
  35. package/dist/cli/commands/__preview-card.js +72 -0
  36. package/dist/cli/commands/__preview-card.js.map +1 -0
  37. package/dist/cli/commands/ask.d.ts +50 -0
  38. package/dist/cli/commands/ask.js +240 -0
  39. package/dist/cli/commands/ask.js.map +1 -0
  40. package/dist/cli/commands/auto.d.ts +12 -0
  41. package/dist/cli/commands/auto.js +28 -0
  42. package/dist/cli/commands/auto.js.map +1 -0
  43. package/dist/cli/commands/cockpit.d.ts +14 -0
  44. package/dist/cli/commands/cockpit.js +52 -0
  45. package/dist/cli/commands/cockpit.js.map +1 -0
  46. package/dist/cli/commands/daemon.d.ts +75 -0
  47. package/dist/cli/commands/daemon.js +258 -0
  48. package/dist/cli/commands/daemon.js.map +1 -0
  49. package/dist/cli/commands/doctor.d.ts +6 -0
  50. package/dist/cli/commands/doctor.js +159 -0
  51. package/dist/cli/commands/doctor.js.map +1 -0
  52. package/dist/cli/commands/grep.d.ts +2 -0
  53. package/dist/cli/commands/grep.js +52 -0
  54. package/dist/cli/commands/grep.js.map +1 -0
  55. package/dist/cli/commands/index-cmd.d.ts +2 -0
  56. package/dist/cli/commands/index-cmd.js +29 -0
  57. package/dist/cli/commands/index-cmd.js.map +1 -0
  58. package/dist/cli/commands/ls.d.ts +30 -0
  59. package/dist/cli/commands/ls.js +95 -0
  60. package/dist/cli/commands/ls.js.map +1 -0
  61. package/dist/cli/commands/overlay.d.ts +26 -0
  62. package/dist/cli/commands/overlay.js +216 -0
  63. package/dist/cli/commands/overlay.js.map +1 -0
  64. package/dist/cli/commands/pick.d.ts +59 -0
  65. package/dist/cli/commands/pick.js +254 -0
  66. package/dist/cli/commands/pick.js.map +1 -0
  67. package/dist/cli/commands/picker-rows.d.ts +17 -0
  68. package/dist/cli/commands/picker-rows.js +47 -0
  69. package/dist/cli/commands/picker-rows.js.map +1 -0
  70. package/dist/cli/commands/resume.d.ts +25 -0
  71. package/dist/cli/commands/resume.js +88 -0
  72. package/dist/cli/commands/resume.js.map +1 -0
  73. package/dist/cli/commands/run.d.ts +13 -0
  74. package/dist/cli/commands/run.js +78 -0
  75. package/dist/cli/commands/run.js.map +1 -0
  76. package/dist/cli/commands/setup.d.ts +37 -0
  77. package/dist/cli/commands/setup.js +200 -0
  78. package/dist/cli/commands/setup.js.map +1 -0
  79. package/dist/cli/commands/show.d.ts +2 -0
  80. package/dist/cli/commands/show.js +24 -0
  81. package/dist/cli/commands/show.js.map +1 -0
  82. package/dist/cli/commands/summarize.d.ts +2 -0
  83. package/dist/cli/commands/summarize.js +59 -0
  84. package/dist/cli/commands/summarize.js.map +1 -0
  85. package/dist/cli/commands/tmux.d.ts +11 -0
  86. package/dist/cli/commands/tmux.js +90 -0
  87. package/dist/cli/commands/tmux.js.map +1 -0
  88. package/dist/cli/commands/watch.d.ts +2 -0
  89. package/dist/cli/commands/watch.js +76 -0
  90. package/dist/cli/commands/watch.js.map +1 -0
  91. package/dist/cli/format.d.ts +74 -0
  92. package/dist/cli/format.js +242 -0
  93. package/dist/cli/format.js.map +1 -0
  94. package/dist/cli/gmux-render.d.ts +11 -0
  95. package/dist/cli/gmux-render.js +60 -0
  96. package/dist/cli/gmux-render.js.map +1 -0
  97. package/dist/cli/main.d.ts +8 -0
  98. package/dist/cli/main.js +145 -0
  99. package/dist/cli/main.js.map +1 -0
  100. package/dist/cli/overlay-ask.d.ts +13 -0
  101. package/dist/cli/overlay-ask.js +32 -0
  102. package/dist/cli/overlay-ask.js.map +1 -0
  103. package/dist/cli/overlay.d.ts +26 -0
  104. package/dist/cli/overlay.js +149 -0
  105. package/dist/cli/overlay.js.map +1 -0
  106. package/dist/cli/picker.d.ts +308 -0
  107. package/dist/cli/picker.js +642 -0
  108. package/dist/cli/picker.js.map +1 -0
  109. package/dist/cli/preview.d.ts +87 -0
  110. package/dist/cli/preview.js +292 -0
  111. package/dist/cli/preview.js.map +1 -0
  112. package/dist/cli/tmux-label.d.ts +50 -0
  113. package/dist/cli/tmux-label.js +146 -0
  114. package/dist/cli/tmux-label.js.map +1 -0
  115. package/dist/core/errors.d.ts +54 -0
  116. package/dist/core/errors.js +91 -0
  117. package/dist/core/errors.js.map +1 -0
  118. package/dist/core/fingerprint.d.ts +21 -0
  119. package/dist/core/fingerprint.js +59 -0
  120. package/dist/core/fingerprint.js.map +1 -0
  121. package/dist/core/gmux-types.d.ts +102 -0
  122. package/dist/core/gmux-types.js +13 -0
  123. package/dist/core/gmux-types.js.map +1 -0
  124. package/dist/core/pane-state.d.ts +5 -0
  125. package/dist/core/pane-state.js +15 -0
  126. package/dist/core/pane-state.js.map +1 -0
  127. package/dist/core/paths.d.ts +68 -0
  128. package/dist/core/paths.js +106 -0
  129. package/dist/core/paths.js.map +1 -0
  130. package/dist/core/proc-tree.d.ts +9 -0
  131. package/dist/core/proc-tree.js +33 -0
  132. package/dist/core/proc-tree.js.map +1 -0
  133. package/dist/core/text.d.ts +34 -0
  134. package/dist/core/text.js +126 -0
  135. package/dist/core/text.js.map +1 -0
  136. package/dist/core/types.d.ts +322 -0
  137. package/dist/core/types.js +10 -0
  138. package/dist/core/types.js.map +1 -0
  139. package/dist/services/ask-transcript.d.ts +256 -0
  140. package/dist/services/ask-transcript.js +544 -0
  141. package/dist/services/ask-transcript.js.map +1 -0
  142. package/dist/services/ask.d.ts +54 -0
  143. package/dist/services/ask.js +161 -0
  144. package/dist/services/ask.js.map +1 -0
  145. package/dist/services/auto-summarize.d.ts +188 -0
  146. package/dist/services/auto-summarize.js +415 -0
  147. package/dist/services/auto-summarize.js.map +1 -0
  148. package/dist/services/concurrency.d.ts +2 -0
  149. package/dist/services/concurrency.js +16 -0
  150. package/dist/services/concurrency.js.map +1 -0
  151. package/dist/services/config.d.ts +131 -0
  152. package/dist/services/config.js +325 -0
  153. package/dist/services/config.js.map +1 -0
  154. package/dist/services/daemon-client.d.ts +9 -0
  155. package/dist/services/daemon-client.js +34 -0
  156. package/dist/services/daemon-client.js.map +1 -0
  157. package/dist/services/daemon-socket.d.ts +30 -0
  158. package/dist/services/daemon-socket.js +76 -0
  159. package/dist/services/daemon-socket.js.map +1 -0
  160. package/dist/services/daemon.d.ts +43 -0
  161. package/dist/services/daemon.js +106 -0
  162. package/dist/services/daemon.js.map +1 -0
  163. package/dist/services/distill.d.ts +39 -0
  164. package/dist/services/distill.js +93 -0
  165. package/dist/services/distill.js.map +1 -0
  166. package/dist/services/guardian.d.ts +26 -0
  167. package/dist/services/guardian.js +71 -0
  168. package/dist/services/guardian.js.map +1 -0
  169. package/dist/services/index-store.d.ts +36 -0
  170. package/dist/services/index-store.js +125 -0
  171. package/dist/services/index-store.js.map +1 -0
  172. package/dist/services/log-rotation.d.ts +3 -0
  173. package/dist/services/log-rotation.js +28 -0
  174. package/dist/services/log-rotation.js.map +1 -0
  175. package/dist/services/pane-links.d.ts +10 -0
  176. package/dist/services/pane-links.js +47 -0
  177. package/dist/services/pane-links.js.map +1 -0
  178. package/dist/services/pane-process.d.ts +95 -0
  179. package/dist/services/pane-process.js +183 -0
  180. package/dist/services/pane-process.js.map +1 -0
  181. package/dist/services/pane-registry.d.ts +24 -0
  182. package/dist/services/pane-registry.js +40 -0
  183. package/dist/services/pane-registry.js.map +1 -0
  184. package/dist/services/provider-process.d.ts +37 -0
  185. package/dist/services/provider-process.js +105 -0
  186. package/dist/services/provider-process.js.map +1 -0
  187. package/dist/services/providers.d.ts +76 -0
  188. package/dist/services/providers.js +104 -0
  189. package/dist/services/providers.js.map +1 -0
  190. package/dist/services/resolve.d.ts +3 -0
  191. package/dist/services/resolve.js +18 -0
  192. package/dist/services/resolve.js.map +1 -0
  193. package/dist/services/resources.d.ts +17 -0
  194. package/dist/services/resources.js +113 -0
  195. package/dist/services/resources.js.map +1 -0
  196. package/dist/services/search.d.ts +29 -0
  197. package/dist/services/search.js +135 -0
  198. package/dist/services/search.js.map +1 -0
  199. package/dist/services/semantic-gate.d.ts +13 -0
  200. package/dist/services/semantic-gate.js +23 -0
  201. package/dist/services/semantic-gate.js.map +1 -0
  202. package/dist/services/semantic.d.ts +113 -0
  203. package/dist/services/semantic.js +216 -0
  204. package/dist/services/semantic.js.map +1 -0
  205. package/dist/services/sensors.d.ts +92 -0
  206. package/dist/services/sensors.js +246 -0
  207. package/dist/services/sensors.js.map +1 -0
  208. package/dist/services/summarize.d.ts +84 -0
  209. package/dist/services/summarize.js +230 -0
  210. package/dist/services/summarize.js.map +1 -0
  211. package/dist/services/tmux-gateway.d.ts +17 -0
  212. package/dist/services/tmux-gateway.js +10 -0
  213. package/dist/services/tmux-gateway.js.map +1 -0
  214. package/dist/services/tmux-resolve.d.ts +54 -0
  215. package/dist/services/tmux-resolve.js +0 -0
  216. package/dist/services/tmux-resolve.js.map +1 -0
  217. package/dist/services/tmux.d.ts +32 -0
  218. package/dist/services/tmux.js +107 -0
  219. package/dist/services/tmux.js.map +1 -0
  220. package/dist/services/views.d.ts +7 -0
  221. package/dist/services/views.js +18 -0
  222. package/dist/services/views.js.map +1 -0
  223. package/dist/services/watch.d.ts +36 -0
  224. package/dist/services/watch.js +98 -0
  225. package/dist/services/watch.js.map +1 -0
  226. package/dist/services/workspace.d.ts +19 -0
  227. package/dist/services/workspace.js +88 -0
  228. package/dist/services/workspace.js.map +1 -0
  229. package/package.json +64 -0
@@ -0,0 +1,161 @@
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 { AskProviderError } from "../core/errors.js";
21
+ import { relativeAge, truncate } from "../core/text.js";
22
+ import { readConfig, resolveAskCommand } from "./config.js";
23
+ import { runProviderCommand } from "./provider-process.js";
24
+ import { onPath } from "./providers.js";
25
+ /**
26
+ * Ask gets longer than a summary does: the model may run several greps before
27
+ * it answers, and each is a subprocess of its own.
28
+ */
29
+ export const ASK_TIMEOUT_MS = 300_000;
30
+ /** How many sessions go into the block. Matches the picker's default window. */
31
+ export const ASK_SESSION_LIMIT = 20;
32
+ /** Per-session caps. A refactor touching 200 files must not eat the prompt. */
33
+ const MAX_FILES_PER_SESSION = 8;
34
+ const MAX_FIELD_CHARS = 240;
35
+ export function buildAskContext(views, focusId = null, limit = ASK_SESSION_LIMIT) {
36
+ const sessions = [...views]
37
+ .sort((a, b) => b.record.updatedAt.localeCompare(a.record.updatedAt))
38
+ .slice(0, limit);
39
+ // Only claim focus on a session that actually made the window — otherwise the
40
+ // prompt would point at a session whose details aren't in it.
41
+ const focused = focusId
42
+ ? (sessions.find((v) => v.record.sessionId.startsWith(focusId))?.record.sessionId ?? null)
43
+ : null;
44
+ return { sessions, focusId: focused };
45
+ }
46
+ /** Short id: what a human types and what the model should quote back. */
47
+ export function shortId(sessionId) {
48
+ return sessionId.slice(0, 8);
49
+ }
50
+ function renderSession(view, index, focusId, now) {
51
+ const { record, summary } = view;
52
+ const lines = [];
53
+ const flags = [
54
+ record.endedMidTask ? "ENDED MID-TASK" : "",
55
+ record.sessionId === focusId ? "← the session the user is looking at" : "",
56
+ ].filter((f) => f !== "");
57
+ const where = record.gitBranch ? `${record.project ?? "?"}/${record.gitBranch}` : (record.project ?? "?");
58
+ lines.push(`### ${index + 1}. ${where} · ${relativeAge(record.updatedAt, now)} ago · id ${shortId(record.sessionId)}${flags.length > 0 ? ` [${flags.join(" · ")}]` : ""}`);
59
+ lines.push(`harness: ${record.harness}`);
60
+ if (summary) {
61
+ lines.push(`headline: ${truncate(summary.headline, MAX_FIELD_CHARS)}`);
62
+ if (summary.overview)
63
+ lines.push(`what it's about: ${truncate(summary.overview, MAX_FIELD_CHARS)}`);
64
+ if (summary.landed)
65
+ lines.push(`landed: ${truncate(summary.landed, MAX_FIELD_CHARS)}`);
66
+ if (summary.open)
67
+ lines.push(`open: ${truncate(summary.open, MAX_FIELD_CHARS)}`);
68
+ if (summary.nextStep)
69
+ lines.push(`next step: ${truncate(summary.nextStep, MAX_FIELD_CHARS)}`);
70
+ }
71
+ else {
72
+ // No summary yet. Say so rather than omitting the session: a gap the model
73
+ // knows about is a caveat it can give you, and one it doesn't is a lie.
74
+ lines.push("summary: NOT YET WRITTEN — the facts below are all we know");
75
+ if (record.title)
76
+ lines.push(`title (recorded at session START, often stale): ${record.title}`);
77
+ if (record.lastUserPrompt) {
78
+ lines.push(`the developer's last instruction: ${truncate(record.lastUserPrompt, MAX_FIELD_CHARS)}`);
79
+ }
80
+ }
81
+ if (record.filesTouched.length > 0) {
82
+ const shown = record.filesTouched.slice(0, MAX_FILES_PER_SESSION);
83
+ const more = record.filesTouched.length - shown.length;
84
+ lines.push(`files: ${shown.join(", ")}${more > 0 ? ` (+${more} more)` : ""}`);
85
+ }
86
+ if (record.lastToolFailure) {
87
+ lines.push(`last failing command: ${truncate(record.lastToolFailure, MAX_FIELD_CHARS)}`);
88
+ }
89
+ return lines.join("\n");
90
+ }
91
+ /**
92
+ * The prompt for one turn.
93
+ *
94
+ * `turns` is the conversation so far. We re-send it because `claude -p` is
95
+ * one-shot and `--resume` is Claude-specific — carrying state in memory and
96
+ * replaying it is what keeps this working for any provider. Bounded, because
97
+ * the context block is.
98
+ */
99
+ export function buildAskPrompt(context, turns, question, now = new Date()) {
100
+ const lines = [
101
+ "You are helping a developer decide what to work on next, using summaries of their recent AI coding-agent sessions.",
102
+ "",
103
+ "Each session below was summarized from the ARC of its transcript — what the developer originally asked for, how the work moved, and where it ended — so the fields describe both what the work IS and where it LANDED. A session's `title` is different: it was recorded in the session's first seconds and never revised, so it names the opening prompt and is usually stale. Trust the summary over the title.",
104
+ "",
105
+ `## The developer's ${context.sessions.length} most recent session${context.sessions.length === 1 ? "" : "s"}`,
106
+ "",
107
+ ];
108
+ if (context.sessions.length === 0) {
109
+ lines.push("(none — the developer has no indexed sessions)");
110
+ }
111
+ else {
112
+ lines.push(context.sessions.map((v, i) => renderSession(v, i, context.focusId, now)).join("\n\n"));
113
+ }
114
+ lines.push("", "## Digging deeper", "", "The summaries above are all you have by default. If answering well needs detail they don't carry — what was actually said, the text of an error, whether something was tried — you can search the full transcripts:", "", " gmux grep '<query>' --json", "", "Useful flags: `-p <project>` narrows to one repo, `-n <count>` caps results, `-e` treats the query as a regex.", "Run it when it would change your answer. Don't run it to confirm something the summaries already say, and don't guess at detail you could have looked up.");
115
+ if (turns.length > 0) {
116
+ lines.push("", "## The conversation so far", "");
117
+ for (const turn of turns) {
118
+ lines.push(`Developer: ${turn.question}`, `You: ${turn.answer}`, "");
119
+ }
120
+ }
121
+ lines.push("", "## The question", "", question, "", "## How to answer", "", "Answer in plain prose for a terminal — no markdown headers, no tables.", "Be specific: name the session (by its short id), the files, the errors. Point at where to look.", "Ground every claim in the evidence above or in what you grep. If the summaries don't support an answer, say what's missing rather than filling the gap.", "Be brief. This is read in a terminal between other work.");
122
+ return lines.join("\n");
123
+ }
124
+ export class CliAskProvider {
125
+ name;
126
+ argv;
127
+ constructor(argv) {
128
+ this.argv = argv;
129
+ this.name = argv.join(" ");
130
+ }
131
+ async isAvailable() {
132
+ const binary = this.argv[0];
133
+ if (!binary)
134
+ return false;
135
+ return onPath(binary);
136
+ }
137
+ async ask(prompt) {
138
+ try {
139
+ const output = await runProviderCommand(this.argv, prompt, { timeoutMs: ASK_TIMEOUT_MS });
140
+ const answer = output.trim();
141
+ if (answer === "")
142
+ throw new Error("reply was empty");
143
+ return answer;
144
+ }
145
+ catch (error) {
146
+ throw new AskProviderError(this.name, error.message);
147
+ }
148
+ }
149
+ }
150
+ /**
151
+ * The ask provider for the current config, or null when the user has said no to
152
+ * model calls.
153
+ *
154
+ * Null is an answer, not a failure — `provider: null` in config means exactly
155
+ * this, and the caller renders it as "run `gmux setup`" rather than as an error.
156
+ */
157
+ export async function defaultAskProvider() {
158
+ const command = resolveAskCommand(await readConfig());
159
+ return command ? new CliAskProvider(command) : null;
160
+ }
161
+ //# sourceMappingURL=ask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ask.js","sourceRoot":"","sources":["../../src/services/ask.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Auto-summarize: keep the top of `gmux ls` written, without ever making you wait.
3
+ *
4
+ * A summary costs a model call (~8s). Ten of them, inline, would turn a 60ms
5
+ * `gmux ls` into a minute of staring at a cursor — so the foreground command never
6
+ * runs one. Instead it decides *what* would need summarizing, hands that job to
7
+ * a DETACHED child process, prints a one-line notice to stderr and exits. The
8
+ * summaries land in the cache and appear on the next run.
9
+ *
10
+ * Three things this module exists to prevent:
11
+ *
12
+ * 1. **Blocking.** The child is spawned `detached` with `stdio: "ignore"` and
13
+ * unref'd, so it outlives its parent and the parent's event loop drains
14
+ * immediately. Nothing is awaited.
15
+ *
16
+ * 2. **A stampede.** Five `gmux ls` in a row must not start five summarizers doing
17
+ * identical work. A lock file in the cache dir is created with O_EXCL before
18
+ * spawning; a lock whose owner is dead — or older than LOCK_STALE_MS — is
19
+ * taken over. A short cooldown stops us re-deciding on every invocation.
20
+ *
21
+ * 3. **A feedback loop.** The provider is `claude -p`, which itself writes a new
22
+ * Claude Code session to disk. If those were eligible targets, gmux
23
+ * would summarize its own summarizer forever. `automated` and `sidechain`
24
+ * sessions are therefore excluded from the target set — see
25
+ * `autoSummarizeCandidates`, which is where that guarantee lives.
26
+ */
27
+ import type { SessionRecord, SummaryProvider } from "../core/types.js";
28
+ import { type SummarizeBatchResult } from "./summarize.js";
29
+ /**
30
+ * How many recent sessions the background pass keeps summarized.
31
+ *
32
+ * This MUST be at least as large as `gmux ls`'s default limit, or the bottom of
33
+ * the default view is permanently `○` and the feature looks broken — which is
34
+ * exactly what happened when this was 10 and `gmux ls` showed 20.
35
+ *
36
+ * Override with `GMUX_AUTO_SUMMARIZE=<n>`.
37
+ */
38
+ export declare const AUTO_SUMMARIZE_LIMIT = 20;
39
+ /**
40
+ * Hard ceiling on one background pass.
41
+ *
42
+ * `gmux ls -n 500` should not fire 500 model calls. Anything beyond this is left
43
+ * for the next run, and we say so rather than truncating silently.
44
+ */
45
+ export declare const MAX_PER_PASS = 50;
46
+ /** A lock older than this belongs to a process that died without cleaning up. */
47
+ export declare const LOCK_STALE_MS: number;
48
+ /** Don't re-decide on every invocation; `gmux ls` in a loop should cost nothing. */
49
+ export declare const COOLDOWN_MS = 60000;
50
+ /** The hidden CLI command the detached worker runs. It never auto-summarizes itself. */
51
+ export declare const AUTO_SUMMARIZE_COMMAND = "__auto-summarize";
52
+ export declare function lockPath(): string;
53
+ export declare function statePath(): string;
54
+ /** The sessions the running worker is writing right now. Drives the `◐` marker. */
55
+ export declare function queuePath(): string;
56
+ /** Where a failing background pass leaves its evidence. */
57
+ export declare function logPath(): string;
58
+ /**
59
+ * Off via `GMUX_AUTO_SUMMARIZE=0`.
60
+ *
61
+ * Background model calls spend tokens. A user who has not opted into that must
62
+ * be able to say no once, in their shell profile, and be done with it.
63
+ */
64
+ export declare function autoSummarizeEnabled(env?: NodeJS.ProcessEnv): boolean;
65
+ /**
66
+ * Every reason the background pass might be off, in one place.
67
+ *
68
+ * Three independent "no"s, and any of them is final:
69
+ *
70
+ * - the env var — a shell-profile opt-out
71
+ * - the config — what the user answered in `gmux setup`
72
+ * - GMUX_CHILD — we are a `gmux` spawned by our own provider
73
+ *
74
+ * The last is the one that isn't obvious. `gmux ask`'s provider may run `gmux grep`,
75
+ * whose postAction hook would start a summarize pass — a model call spawned by a
76
+ * model call. The lock would absorb most of the damage; this makes it a
77
+ * non-question.
78
+ */
79
+ export declare function autoSummarizeAllowedNow(env?: NodeJS.ProcessEnv): Promise<boolean>;
80
+ export interface AutoSummarizeLock {
81
+ /** Pid of the worker. 0 when the lock is taken but the child is not spawned yet. */
82
+ pid: number;
83
+ startedAt: string;
84
+ }
85
+ /** A lock is stale when its owner is gone, or when it is simply too old. */
86
+ export declare function isLockStale(lock: AutoSummarizeLock, now?: Date, staleMs?: number): boolean;
87
+ export declare function readLock(): Promise<AutoSummarizeLock | null>;
88
+ /**
89
+ * Take the lock, or report that someone else holds it.
90
+ *
91
+ * The create is O_EXCL, so two `gmux` processes racing cannot both win. A stale
92
+ * lock is removed and re-created rather than overwritten, so the takeover is
93
+ * itself a race whose loser backs off instead of double-spawning.
94
+ */
95
+ export declare function acquireLock(now?: Date): Promise<boolean>;
96
+ export declare function releaseLock(): Promise<void>;
97
+ /** Record that we just decided; suppresses the next COOLDOWN_MS worth of checks. */
98
+ export declare function noteCheck(now?: Date): Promise<void>;
99
+ export declare function inCooldown(now?: Date, cooldownMs?: number): Promise<boolean>;
100
+ export interface AutoSummarizeQueue {
101
+ ids: string[];
102
+ startedAt: string;
103
+ }
104
+ /** Publish what the worker is about to write, so the list can mark those rows `◐`. */
105
+ export declare function writeQueue(ids: readonly string[], now?: Date): Promise<void>;
106
+ export declare function readQueue(): Promise<AutoSummarizeQueue | null>;
107
+ export declare function clearQueue(): Promise<void>;
108
+ /**
109
+ * Sessions being summarized *right now*, for the `◐` marker.
110
+ *
111
+ * Guarded by the lock: a queue left behind by a crashed worker would otherwise
112
+ * mark rows as in-progress forever. No live lock, no in-progress rows.
113
+ */
114
+ export declare function inProgressIds(now?: Date): Promise<Set<string>>;
115
+ /**
116
+ * The sessions the background pass is allowed to touch.
117
+ *
118
+ * THE FEEDBACK-LOOP GUARD. `claude -p` — our own summary provider — writes a
119
+ * Claude Code session for every summary it produces, flagged `isAutomated`.
120
+ * Summarizing those would generate more of them, forever, at real token cost.
121
+ * Sidechains are excluded for the same reason plus a better one: nobody resumes
122
+ * a subagent transcript.
123
+ *
124
+ * Pure. This is the function the anti-loop test pins.
125
+ */
126
+ export declare function autoSummarizeCandidates(records: readonly SessionRecord[], limit?: number): SessionRecord[];
127
+ /**
128
+ * Of the recent, eligible sessions, the ones whose summary is missing or stale.
129
+ *
130
+ * Reads the summary cache but calls no model, so tests exercise it for real.
131
+ */
132
+ export declare function selectAutoSummarizeTargets(records: readonly SessionRecord[], limit?: number): Promise<SessionRecord[]>;
133
+ export type AutoSummarizeStatus = "spawned" | "disabled" | "no-provider" | "locked" | "cooling-down" | "nothing-to-do" | "spawn-failed";
134
+ export interface AutoSummarizeOutcome {
135
+ status: AutoSummarizeStatus;
136
+ /** How many sessions the worker was asked to write. */
137
+ targets: number;
138
+ /** Their ids — the caller renders these as `◐` on this very run. */
139
+ targetIds: string[];
140
+ }
141
+ export interface MaybeAutoSummarizeOptions {
142
+ /**
143
+ * The sessions the command just displayed.
144
+ *
145
+ * Passing these is what makes `gmux ls -n 50` summarize all fifty: the window
146
+ * follows what you actually looked at, rather than a fixed top-N that leaves
147
+ * the bottom of your screen permanently un-summarized.
148
+ */
149
+ records?: readonly SessionRecord[];
150
+ /** False when `--no-auto-summarize` was passed. */
151
+ enabled?: boolean;
152
+ /**
153
+ * Skip the cooldown — and only the cooldown.
154
+ *
155
+ * Set when the user explicitly asked for a refresh (ctrl-r in the picker).
156
+ * The cooldown guards against *incidental* re-decisions, like `gmux ls` in a
157
+ * loop; a keypress is not incidental, and a key that silently does nothing
158
+ * for its first minute reads as broken.
159
+ *
160
+ * The lock still applies, so hammering the key cannot start two workers, and
161
+ * `GMUX_AUTO_SUMMARIZE=0` still wins: force overrides our own
162
+ * optimisation, never the user's opt-out.
163
+ */
164
+ force?: boolean;
165
+ now?: Date;
166
+ /** Injected in tests. Never a real model. */
167
+ provider?: SummaryProvider;
168
+ /** Injected in tests, so no test ever spawns anything. Returns the child pid. */
169
+ spawnWorker?: () => number | undefined;
170
+ /** Where the notice goes. STDERR — stdout must stay clean for `--json` and pipes. */
171
+ notify?: (message: string) => void;
172
+ }
173
+ /**
174
+ * Decide whether to kick off a background summarize pass, and if so, do it.
175
+ *
176
+ * Never throws and never blocks on a model: every failure path is a silent skip.
177
+ * A read command must not die because `claude` is missing or a cache file is odd.
178
+ */
179
+ export declare function maybeAutoSummarize(options?: MaybeAutoSummarizeOptions): Promise<AutoSummarizeOutcome>;
180
+ /**
181
+ * The worker body. Runs in the detached child, holding the lock its parent took.
182
+ *
183
+ * Re-selects its own targets rather than trusting a list handed down an argv: by
184
+ * the time it starts, the parent's view of the store is already stale.
185
+ */
186
+ export declare function runAutoSummarize(provider: SummaryProvider): Promise<SummarizeBatchResult>;
187
+ /** The most recent background failure, if any — surfaced by `gmux doctor`. */
188
+ export declare function lastAutoSummarizeError(): Promise<string | null>;