glimmervoid 0.39.0 → 0.40.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/README.md +1 -1
- package/dist/bin/glimmervoid.js +22 -25
- package/dist/chunks/{adapters-B9tP9MXC.js → adapters-B6tbX3Aj.js} +4 -3
- package/dist/chunks/{adapters-Buu6yI71.js → adapters-CXQfLoyj.js} +1 -1
- package/dist/chunks/agent-api-cli-BBech5lF.js +1 -0
- package/dist/chunks/{agent-api-cli-2_RwTTTJ.js → agent-api-cli-BNHJTmNW.js} +46 -17
- package/dist/chunks/agent-api-core-DDrJQEdE.js +304 -0
- package/dist/chunks/{agent-setup-cli-C1nFdgFg.js → agent-setup-cli-CeBBwKuL.js} +1 -1
- package/dist/chunks/config-store-BZpN2djp.js +1 -0
- package/dist/chunks/{config-store-n23JDU4E.js → config-store-DqMScSdg.js} +20 -1
- package/dist/chunks/contracts-CloExMlS.js +680 -0
- package/dist/chunks/{contracts-DYQW11de.js → control-messages-DpBaniJi.js} +1201 -1794
- package/dist/chunks/{git-version-DXozXVnb.js → git-version-CJQMfxtZ.js} +1 -1
- package/dist/chunks/{grok-DeGAXgZF.js → grok-BLzhApwT.js} +1 -1
- package/dist/chunks/{main-D4uwk-_M.js → main-DDU8IvFD.js} +26436 -24806
- package/dist/chunks/{pair-cli-D9DZBqk0.js → pair-cli-CsOVTQGJ.js} +2 -2
- package/dist/chunks/{remote-config-DiLwnzBq.js → remote-config-HlABuLTp.js} +1 -1
- package/dist/chunks/{session-D2EbiysG.js → session-BwB43SCa.js} +103 -43
- package/dist/chunks/{session-routes-BQAZzMKZ.js → session-routes-DNJzsckH.js} +23 -3
- package/dist/chunks/{settings-cli-DP4t-I9U.js → settings-cli-DkkbK-dj.js} +30 -156
- package/dist/chunks/settings-readout-core-DrXT4aWm.js +201 -0
- package/dist/chunks/{ssh-sign-cli-D2RIs-iN.js → ssh-sign-cli-CYeKRevK.js} +2 -2
- package/dist/client/assets/index-BK9jLR47.js +148 -0
- package/dist/client/assets/index-CU_uqJmi.css +2 -0
- package/dist/client/index.html +4 -3
- package/dist/server/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunks/agent-api-cli-PfCov-qb.js +0 -1
- package/dist/chunks/agent-api-core-D2HLgBEe.js +0 -180
- package/dist/chunks/config-store-8Z9vkWvf.js +0 -1
- package/dist/client/assets/index-C0YUJwjz.css +0 -2
- package/dist/client/assets/index-w8lG-tk2.js +0 -148
package/README.md
CHANGED
|
@@ -76,7 +76,7 @@ Declare another agent under `customAgents` in `config.json` with an `id`, `label
|
|
|
76
76
|
|
|
77
77
|
## Features
|
|
78
78
|
|
|
79
|
-
- **Focus view.** A rail of every session on the left, the selected session's live terminal in the middle, its changes on the right.
|
|
79
|
+
- **Focus view.** A rail of every session on the left, the selected session's live terminal in the middle, its changes on the right. The top of the rail counts what needs you, and `Cmd/Alt+J` jumps straight to the next one from any tab.
|
|
80
80
|
- **A worktree per agent.** Every session in a git repo gets its own worktree, so agents never step on your checkout or each other. Review the diff and merge with one click while the agent keeps working. A conflicted merge parks, and **Resolve in session** hands it back to the agent that wrote it.
|
|
81
81
|
- **Exact status.** Hooks report every state change and the terminal title fills the gaps. The screen is never scraped ([how](#how-status-detection-works)).
|
|
82
82
|
- **Notifications.** Browser notifications when a session needs input, finishes or fails, with your own alert sounds if you like (drop `.ogg`, `.mp3`, `.wav`, `.m4a` or `.webm` files into `~/.glimmervoid/sounds/`). Telegram covers you when no dashboard is open.
|
package/dist/bin/glimmervoid.js
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import { o as nodePtyRebuildHint, r as firstLine, t as probeNodePty } from "../chunks/node-pty-preflight-C8QIlrtU.js";
|
|
3
3
|
import { i as errorText } from "../chunks/text-DoirglWV.js";
|
|
4
4
|
import { a as packageRoot } from "../chunks/runtime-paths-BkeftE60.js";
|
|
5
|
-
import { t as AGENT_API_VERBS } from "../chunks/session-
|
|
5
|
+
import { t as AGENT_API_VERBS } from "../chunks/session-BwB43SCa.js";
|
|
6
6
|
import { i as execSync } from "../chunks/child-process-safe-CbQnyh7D.js";
|
|
7
|
+
import { r as agentCommandHelpLines } from "../chunks/agent-api-core-DDrJQEdE.js";
|
|
7
8
|
import { t as renderTable } from "../chunks/ascii-figure-core-pDAwtlKO.js";
|
|
8
9
|
import { n as glimmervoidHomeDir, t as decideConfigPath } from "../chunks/config-path-core-DFt-TemS.js";
|
|
9
|
-
import { i as parseCommandLine, r as gitDoctorRows, t as probeGitVersion } from "../chunks/git-version-
|
|
10
|
+
import { i as parseCommandLine, r as gitDoctorRows, t as probeGitVersion } from "../chunks/git-version-CJQMfxtZ.js";
|
|
10
11
|
import { i as guardrailsDoctorRows, r as probeGuardrailsDependencies } from "../chunks/guardrails-deps-CLxK4J-g.js";
|
|
11
12
|
import { i as pnpmGlobalBinDir, n as npmGlobalBinDir, r as onPath, t as formatPathNotice } from "../chunks/path-doctor-C23VAqeu.js";
|
|
12
13
|
import fs from "node:fs";
|
|
@@ -35,12 +36,7 @@ Editor integration (Visions):
|
|
|
35
36
|
visions relay Run the Visions LSP relay on stdio (an editor's LSP client starts this)
|
|
36
37
|
|
|
37
38
|
Inside a Glimmervoid session:
|
|
38
|
-
|
|
39
|
-
attention <note> Flag this session as needing the operator
|
|
40
|
-
board List the live sessions
|
|
41
|
-
push Land this session's commits on the integration branch and push it
|
|
42
|
-
edit <path> Open a text file for the operator to edit in the dashboard
|
|
43
|
-
limits List which of these commands answer here and what the guardrails block
|
|
39
|
+
${agentCommandHelpLines().join("\n")}
|
|
44
40
|
ssh-sign Sign a commit with the operator's forwarded ssh agent (git runs this)
|
|
45
41
|
|
|
46
42
|
Options:
|
|
@@ -63,36 +59,37 @@ if (commandLine.kind === "version") {
|
|
|
63
59
|
}
|
|
64
60
|
if (commandLine.globalOptions.configPath) process.env.GLIMMERVOID_CONFIG = commandLine.globalOptions.configPath;
|
|
65
61
|
if (commandLine.globalOptions.port) process.env.GLIMMERVOID_PORT = commandLine.globalOptions.port;
|
|
66
|
-
var
|
|
62
|
+
var OWN_SUBCOMMAND_RUNNERS = [
|
|
67
63
|
["doctor", async () => {
|
|
68
64
|
await runDoctor();
|
|
69
65
|
return 0;
|
|
70
66
|
}],
|
|
71
67
|
["pair", async (subcommandArgs) => {
|
|
72
|
-
const { runPairCli } = await import("../chunks/pair-cli-
|
|
68
|
+
const { runPairCli } = await import("../chunks/pair-cli-CsOVTQGJ.js");
|
|
73
69
|
return runPairCli(subcommandArgs);
|
|
74
70
|
}],
|
|
75
71
|
["agent", async (subcommandArgs) => {
|
|
76
|
-
const { runAgentSetupCli } = await import("../chunks/agent-setup-cli-
|
|
72
|
+
const { runAgentSetupCli } = await import("../chunks/agent-setup-cli-CeBBwKuL.js");
|
|
77
73
|
return runAgentSetupCli(subcommandArgs);
|
|
78
74
|
}],
|
|
79
75
|
["settings", async (subcommandArgs) => {
|
|
80
|
-
const {
|
|
81
|
-
return
|
|
76
|
+
const { runSettingsCli } = await import("../chunks/settings-cli-DkkbK-dj.js");
|
|
77
|
+
return runSettingsCli(subcommandArgs);
|
|
82
78
|
}],
|
|
83
79
|
["visions", async (subcommandArgs) => {
|
|
84
80
|
const { runVisionsCli } = await import("../chunks/visions-cli-CYujESdJ.js");
|
|
85
81
|
return runVisionsCli(subcommandArgs);
|
|
86
82
|
}],
|
|
87
83
|
["ssh-sign", async (subcommandArgs) => {
|
|
88
|
-
const { runSshSignCli } = await import("../chunks/ssh-sign-cli-
|
|
84
|
+
const { runSshSignCli } = await import("../chunks/ssh-sign-cli-CYeKRevK.js");
|
|
89
85
|
return runSshSignCli(subcommandArgs);
|
|
90
|
-
}]
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
]);
|
|
86
|
+
}]
|
|
87
|
+
];
|
|
88
|
+
var ownSubcommandNames = new Set(OWN_SUBCOMMAND_RUNNERS.map(([name]) => name));
|
|
89
|
+
var SUBCOMMAND_RUNNERS = new Map([...OWN_SUBCOMMAND_RUNNERS, ...AGENT_API_VERBS.filter((verb) => !ownSubcommandNames.has(verb)).map((verb) => [verb, async (subcommandArgs, subcommandName) => {
|
|
90
|
+
const { runAgentApiCli } = await import("../chunks/agent-api-cli-BBech5lF.js");
|
|
91
|
+
return runAgentApiCli([subcommandName, ...subcommandArgs]);
|
|
92
|
+
}])]);
|
|
96
93
|
var AGENT_INSTALL_COMMAND_BY_ID = {
|
|
97
94
|
"claude-code": "npm install -g @anthropic-ai/claude-code",
|
|
98
95
|
codex: "npm install -g @openai/codex"
|
|
@@ -104,7 +101,7 @@ function missingAgentText(agentId) {
|
|
|
104
101
|
}
|
|
105
102
|
async function printSessionLimits() {
|
|
106
103
|
if (!process.env["GLIMMERVOID_AGENT_URL"]) return;
|
|
107
|
-
const { sessionLimitsHelpLines } = await import("../chunks/agent-api-cli-
|
|
104
|
+
const { sessionLimitsHelpLines } = await import("../chunks/agent-api-cli-BBech5lF.js");
|
|
108
105
|
const limitsLines = await sessionLimitsHelpLines();
|
|
109
106
|
console.log(`\n${limitsLines.join("\n")}`);
|
|
110
107
|
}
|
|
@@ -161,7 +158,7 @@ async function readDeclaredCustomAgents() {
|
|
|
161
158
|
declared: [],
|
|
162
159
|
error: null
|
|
163
160
|
};
|
|
164
|
-
const { loadConfigFile } = await import("../chunks/config-store-
|
|
161
|
+
const { loadConfigFile } = await import("../chunks/config-store-BZpN2djp.js");
|
|
165
162
|
try {
|
|
166
163
|
const loaded = loadConfigFile(decided.path, { exitOnError: false });
|
|
167
164
|
if (!loaded.config) return {
|
|
@@ -237,7 +234,7 @@ async function runDoctor() {
|
|
|
237
234
|
}
|
|
238
235
|
switchSection("Agents");
|
|
239
236
|
try {
|
|
240
|
-
const { listAgentIds, describeAgentResolvability, setCustomAgents } = await import("../chunks/adapters-
|
|
237
|
+
const { listAgentIds, describeAgentResolvability, setCustomAgents } = await import("../chunks/adapters-CXQfLoyj.js");
|
|
241
238
|
const declaredCustomAgents = await readDeclaredCustomAgents();
|
|
242
239
|
if (declaredCustomAgents.error) line("config", declaredCustomAgents.error);
|
|
243
240
|
setCustomAgents(declaredCustomAgents.declared);
|
|
@@ -245,7 +242,7 @@ async function runDoctor() {
|
|
|
245
242
|
const { label, path: resolvedPath } = describeAgentResolvability(id);
|
|
246
243
|
line(`${id} (${label})`, resolvedPath ?? missingAgentText(id));
|
|
247
244
|
}
|
|
248
|
-
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-
|
|
245
|
+
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-CeBBwKuL.js");
|
|
249
246
|
const grokSetup = inspectGrokAgentSetup();
|
|
250
247
|
line("grok hook setup", `${grokSetup.classification}: ${grokSetup.filePath}`);
|
|
251
248
|
} catch (err) {
|
|
@@ -262,7 +259,7 @@ async function runDoctor() {
|
|
|
262
259
|
try {
|
|
263
260
|
const { resolvePackageBin } = await import("../chunks/runtime-paths-DSmormp7.js");
|
|
264
261
|
const { execFileSync } = await import("../chunks/child-process-safe-ChRmWAJP.js");
|
|
265
|
-
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-
|
|
262
|
+
const { inspectGrokAgentSetup } = await import("../chunks/agent-setup-cli-CeBBwKuL.js");
|
|
266
263
|
const binPath = resolvePackageBin("cc-safety-net", "cc-safety-net");
|
|
267
264
|
line("Sane YOLO", binPath || "not installed");
|
|
268
265
|
if (binPath) line("cc-safety-net version", execFileSync(process.execPath, [binPath, "--version"], {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { o as relayPath } from "./runtime-paths-BkeftE60.js";
|
|
2
2
|
import { r as execFileSync } from "./child-process-safe-CbQnyh7D.js";
|
|
3
|
+
import "./control-messages-DpBaniJi.js";
|
|
4
|
+
import { o as supervisionLine } from "./session-routes-DNJzsckH.js";
|
|
3
5
|
import { n as resolveAgentCommand, t as buildAgentSpawnCommand } from "./spawn-command-Cc40FiZI.js";
|
|
4
6
|
import { s as rawTextOr } from "./coerce-NSst8oRP.js";
|
|
5
7
|
import { r as nextBackoffMs } from "./backoff-DjVpfHk7.js";
|
|
6
|
-
import {
|
|
7
|
-
import { i as isSpinnerChar, n as classifyAgentTitle, r as isBrailleChar, s as buildAgentEnv, t as grok } from "./grok-DeGAXgZF.js";
|
|
8
|
+
import { i as isSpinnerChar, n as classifyAgentTitle, r as isBrailleChar, s as buildAgentEnv, t as grok } from "./grok-BLzhApwT.js";
|
|
8
9
|
import { t as buildHookCommand } from "./hook-command-core-qTNK-xMF.js";
|
|
9
|
-
import "./contracts-
|
|
10
|
+
import "./contracts-CloExMlS.js";
|
|
10
11
|
import { n as mergeCodexPreToolUse, t as HOOK_TOOLS } from "./hook-tools-Dl7-r2Eb.js";
|
|
11
12
|
import fs from "node:fs";
|
|
12
13
|
import path from "node:path";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { a as describeAgentResolvability, l as listAgentIds, f as setCustomAgents } from "./adapters-
|
|
1
|
+
export { a as describeAgentResolvability, l as listAgentIds, f as setCustomAgents } from "./adapters-B6tbX3Aj.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as askAgentApi, r as runAgentApiCli, i as sessionLimitsHelpLines } from "./agent-api-cli-BNHJTmNW.js";
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { t as errorCode } from "./text-DoirglWV.js";
|
|
2
|
-
import { n as AGENT_URL_ENV, o as AgentLimitsReply } from "./session-
|
|
2
|
+
import { n as AGENT_URL_ENV, o as AgentLimitsReply } from "./session-BwB43SCa.js";
|
|
3
|
+
import { i as formatSessionLimits } from "./session-routes-DNJzsckH.js";
|
|
4
|
+
import { l as isAgentApiVerb, t as AGENT_COMMAND_HELP } from "./agent-api-core-DDrJQEdE.js";
|
|
3
5
|
import { i as resolveHookTarget } from "./hook-relay-core-DA_bz2KA.js";
|
|
4
6
|
import { n as decideGuardrailsProxy } from "./guardrails-proxy-core-BI3rdY-m.js";
|
|
5
7
|
import { t as parseJsonOrNull } from "./json-core-dfa_7Fak.js";
|
|
6
|
-
import { a as formatSessionLimits } from "./session-routes-BQAZzMKZ.js";
|
|
7
|
-
import { l as isAgentApiVerb } from "./agent-api-core-D2HLgBEe.js";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import http from "node:http";
|
|
10
10
|
//#region server/agent-api-cli.ts
|
|
11
11
|
var AGENT_PATH_PREFIX = "/agent/";
|
|
12
12
|
var HELP_LIMITS_TIMEOUT_MS = 2e3;
|
|
13
13
|
var HELP_LIMITS_UNKNOWN_LINE = "This session: Glimmervoid did not report its limits, so run glimmervoid limits to see why";
|
|
14
|
+
function pullRequestBody(rest) {
|
|
15
|
+
const pullRequest = rest.join(" ").trim();
|
|
16
|
+
return pullRequest ? { pr: pullRequest } : null;
|
|
17
|
+
}
|
|
14
18
|
var BODY_BUILDERS = {
|
|
15
19
|
spawn: (rest) => {
|
|
16
20
|
const prompt = rest.join(" ").trim();
|
|
@@ -26,7 +30,15 @@ var BODY_BUILDERS = {
|
|
|
26
30
|
const docPath = rest.join(" ").trim();
|
|
27
31
|
return docPath ? { path: path.resolve(docPath) } : null;
|
|
28
32
|
},
|
|
29
|
-
limits: () => ({})
|
|
33
|
+
limits: () => ({}),
|
|
34
|
+
review: pullRequestBody,
|
|
35
|
+
reviews: () => ({}),
|
|
36
|
+
prs: () => ({}),
|
|
37
|
+
"link-pr": pullRequestBody,
|
|
38
|
+
settings: ([command, settingPath]) => ({
|
|
39
|
+
command,
|
|
40
|
+
...settingPath === void 0 ? {} : { path: settingPath }
|
|
41
|
+
})
|
|
30
42
|
};
|
|
31
43
|
function bodySaysOk(text) {
|
|
32
44
|
try {
|
|
@@ -148,21 +160,12 @@ async function runAgentApiCli(args) {
|
|
|
148
160
|
}
|
|
149
161
|
const body = BODY_BUILDERS[verb](args.slice(1));
|
|
150
162
|
if (!body) {
|
|
151
|
-
|
|
152
|
-
console.error("glimmervoid edit: this command needs a file path, for example \"glimmervoid edit drafts/post.md\"");
|
|
153
|
-
return 1;
|
|
154
|
-
}
|
|
155
|
-
console.error(`glimmervoid ${verb}: this command needs text, for example "glimmervoid ${verb} check the failing test"`);
|
|
163
|
+
console.error(AGENT_COMMAND_HELP[verb].missingArgumentHint ?? `glimmervoid ${verb}: this command needs an argument`);
|
|
156
164
|
return 1;
|
|
157
165
|
}
|
|
158
|
-
const
|
|
159
|
-
if (!rawUrl) {
|
|
160
|
-
console.error(`glimmervoid: ${AGENT_URL_ENV} is not set, so this is not a Glimmervoid session`);
|
|
161
|
-
return 1;
|
|
162
|
-
}
|
|
163
|
-
const target = verbEndpoint(rawUrl, verb);
|
|
166
|
+
const target = sessionEndpoint(verb);
|
|
164
167
|
if (!target.ok) {
|
|
165
|
-
console.error(
|
|
168
|
+
console.error(target.error);
|
|
166
169
|
return 1;
|
|
167
170
|
}
|
|
168
171
|
const reply = await postToAgentIngress({
|
|
@@ -173,5 +176,31 @@ async function runAgentApiCli(args) {
|
|
|
173
176
|
console.log(printableReply(verb, reply.text));
|
|
174
177
|
return reply.hasSuccessStatus && bodySaysOk(reply.text) ? 0 : 1;
|
|
175
178
|
}
|
|
179
|
+
function sessionEndpoint(verb) {
|
|
180
|
+
const rawUrl = process.env[AGENT_URL_ENV];
|
|
181
|
+
if (!rawUrl) return {
|
|
182
|
+
ok: false,
|
|
183
|
+
error: `glimmervoid: ${AGENT_URL_ENV} is not set, so this is not a Glimmervoid session`
|
|
184
|
+
};
|
|
185
|
+
const target = verbEndpoint(rawUrl, verb);
|
|
186
|
+
if (!target.ok) return {
|
|
187
|
+
ok: false,
|
|
188
|
+
error: `glimmervoid: ${AGENT_URL_ENV} must be a loopback http url under ${AGENT_PATH_PREFIX}, so nothing was sent`
|
|
189
|
+
};
|
|
190
|
+
return target;
|
|
191
|
+
}
|
|
192
|
+
async function askAgentApi(verb, body) {
|
|
193
|
+
const target = sessionEndpoint(verb);
|
|
194
|
+
if (!target.ok) return {
|
|
195
|
+
ok: false,
|
|
196
|
+
error: target.error
|
|
197
|
+
};
|
|
198
|
+
const reply = await postToAgentIngress({
|
|
199
|
+
endpoint: target.endpoint,
|
|
200
|
+
token: target.token,
|
|
201
|
+
body
|
|
202
|
+
});
|
|
203
|
+
return parseJsonOrNull(reply.text);
|
|
204
|
+
}
|
|
176
205
|
//#endregion
|
|
177
|
-
export {
|
|
206
|
+
export { sessionLimitsHelpLines as i, postToAgentIngress as n, runAgentApiCli as r, askAgentApi as t };
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { a as AgentEditRequest, i as AgentAttentionRequest, l as AgentSettingsRequest, s as AgentPullRequestRequest, t as AGENT_API_VERBS, u as AgentSpawnRequest } from "./session-BwB43SCa.js";
|
|
2
|
+
import { a as pullRequestKeyFromReference } from "./control-messages-DpBaniJi.js";
|
|
3
|
+
import { n as NO_OWN_WORKTREE_REASON, t as COMMAND_ROUTES } from "./session-routes-DNJzsckH.js";
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
var MAX_SPAWNS_IN_FLIGHT = 1;
|
|
6
|
+
var REFUSAL_REASON = "unknown session";
|
|
7
|
+
var INVALID_BODY_STATUS = 400;
|
|
8
|
+
var ROLE_REFUSED_STATUS = 403;
|
|
9
|
+
var PUSH_REFUSED_STATUS = 409;
|
|
10
|
+
var PUSH_REFUSAL_COPY = Object.freeze({
|
|
11
|
+
"no-worktree": () => NO_OWN_WORKTREE_REASON,
|
|
12
|
+
"merge-in-progress": () => "a merge for this session is already running, so try again when it ends",
|
|
13
|
+
"not-continuable": () => "this session cannot be merged in its current state",
|
|
14
|
+
destroyed: () => "this session is ending",
|
|
15
|
+
"nothing-to-commit": () => "no commit on this branch is missing from the integration branch, so commit your work first",
|
|
16
|
+
"rebase-conflict": ({ base, conflicts = [] }) => `this branch conflicts with ${base} in ${conflicts.join(", ") || "one or more files"}, so rebase onto ${base}, resolve the conflicts, commit, then push again`,
|
|
17
|
+
"not-fast-forward": ({ base }) => `${base} could not be fast-forwarded to this branch, and a push is never forced`,
|
|
18
|
+
"base-diverged": ({ base }) => `the local ${base} and origin/${base} have diverged, which the operator has to reconcile`
|
|
19
|
+
});
|
|
20
|
+
function tokensMatch(presented, expected) {
|
|
21
|
+
if (presented.length !== expected.length) return false;
|
|
22
|
+
try {
|
|
23
|
+
return crypto.timingSafeEqual(Buffer.from(presented, "utf8"), Buffer.from(expected, "utf8"));
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function refused() {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
status: 404,
|
|
32
|
+
reason: REFUSAL_REASON
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function decideAgentRequest({ sessionId, presentedToken, expectedToken, isLoopback }) {
|
|
36
|
+
if (!isLoopback) return refused();
|
|
37
|
+
if (!sessionId) return refused();
|
|
38
|
+
if (!expectedToken) return refused();
|
|
39
|
+
if (!presentedToken) return refused();
|
|
40
|
+
if (!tokensMatch(presentedToken, expectedToken)) return refused();
|
|
41
|
+
return { ok: true };
|
|
42
|
+
}
|
|
43
|
+
function decideSpawnAllowance({ liveChildren, lifetimeSpawns, inFlight }) {
|
|
44
|
+
if (inFlight >= MAX_SPAWNS_IN_FLIGHT) return {
|
|
45
|
+
ok: false,
|
|
46
|
+
status: 429,
|
|
47
|
+
reason: "a spawn is already in flight"
|
|
48
|
+
};
|
|
49
|
+
if (liveChildren >= 3) return {
|
|
50
|
+
ok: false,
|
|
51
|
+
status: 429,
|
|
52
|
+
reason: `at most 3 spawned sessions may be live at once`
|
|
53
|
+
};
|
|
54
|
+
if (lifetimeSpawns >= 10) return {
|
|
55
|
+
ok: false,
|
|
56
|
+
status: 429,
|
|
57
|
+
reason: `this session has used its 10 spawns`
|
|
58
|
+
};
|
|
59
|
+
return { ok: true };
|
|
60
|
+
}
|
|
61
|
+
function describeRoleRefusal(verb, caller) {
|
|
62
|
+
const route = COMMAND_ROUTES[verb](caller);
|
|
63
|
+
if (route.answers) return null;
|
|
64
|
+
if (route.code) return {
|
|
65
|
+
status: PUSH_REFUSED_STATUS,
|
|
66
|
+
body: {
|
|
67
|
+
ok: false,
|
|
68
|
+
reason: route.code,
|
|
69
|
+
error: route.reason
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
status: ROLE_REFUSED_STATUS,
|
|
74
|
+
body: {
|
|
75
|
+
ok: false,
|
|
76
|
+
error: route.reason
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function isAgentApiVerb(verb) {
|
|
81
|
+
return AGENT_API_VERBS.includes(verb);
|
|
82
|
+
}
|
|
83
|
+
function refuseInvalidBody(issues) {
|
|
84
|
+
return {
|
|
85
|
+
ok: false,
|
|
86
|
+
status: INVALID_BODY_STATUS,
|
|
87
|
+
error: issues[0]?.message || "invalid request"
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function bodyParser(schema) {
|
|
91
|
+
return (payload) => {
|
|
92
|
+
const parsed = schema.safeParse(payload);
|
|
93
|
+
if (!parsed.success) return refuseInvalidBody(parsed.error.issues);
|
|
94
|
+
return {
|
|
95
|
+
ok: true,
|
|
96
|
+
request: parsed.data
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
var parseEmptyBody = () => ({
|
|
101
|
+
ok: true,
|
|
102
|
+
request: {}
|
|
103
|
+
});
|
|
104
|
+
var parsePullRequestBody = (payload) => {
|
|
105
|
+
const parsed = AgentPullRequestRequest.safeParse(payload);
|
|
106
|
+
if (!parsed.success) return refuseInvalidBody(parsed.error.issues);
|
|
107
|
+
const pullRequestKey = pullRequestKeyFromReference(parsed.data.pr);
|
|
108
|
+
if (!pullRequestKey) return {
|
|
109
|
+
ok: false,
|
|
110
|
+
status: INVALID_BODY_STATUS,
|
|
111
|
+
error: "pr must be a GitHub pull request url or owner/repo#number"
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
ok: true,
|
|
115
|
+
request: { pullRequestKey }
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
var REQUEST_PARSERS = Object.freeze({
|
|
119
|
+
spawn: bodyParser(AgentSpawnRequest),
|
|
120
|
+
attention: bodyParser(AgentAttentionRequest),
|
|
121
|
+
board: parseEmptyBody,
|
|
122
|
+
push: parseEmptyBody,
|
|
123
|
+
edit: bodyParser(AgentEditRequest),
|
|
124
|
+
limits: parseEmptyBody,
|
|
125
|
+
review: parsePullRequestBody,
|
|
126
|
+
reviews: parseEmptyBody,
|
|
127
|
+
prs: parseEmptyBody,
|
|
128
|
+
"link-pr": parsePullRequestBody,
|
|
129
|
+
settings: bodyParser(AgentSettingsRequest)
|
|
130
|
+
});
|
|
131
|
+
function parseAgentVerb(verb, payload) {
|
|
132
|
+
if (!isAgentApiVerb(verb)) return {
|
|
133
|
+
ok: false,
|
|
134
|
+
status: 404,
|
|
135
|
+
error: REFUSAL_REASON
|
|
136
|
+
};
|
|
137
|
+
const parsed = REQUEST_PARSERS[verb](payload);
|
|
138
|
+
if (!parsed.ok) return parsed;
|
|
139
|
+
return {
|
|
140
|
+
ok: true,
|
|
141
|
+
verb,
|
|
142
|
+
request: parsed.request
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function describePushLanding(landing) {
|
|
146
|
+
if (!landing.merged) {
|
|
147
|
+
const reason = landing.reason || "unknown";
|
|
148
|
+
const explain = PUSH_REFUSAL_COPY[reason];
|
|
149
|
+
return {
|
|
150
|
+
status: PUSH_REFUSED_STATUS,
|
|
151
|
+
body: {
|
|
152
|
+
ok: false,
|
|
153
|
+
reason,
|
|
154
|
+
error: explain ? explain(landing) : `the merge stopped: ${reason}`
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (landing.pushed) return {
|
|
159
|
+
status: 200,
|
|
160
|
+
body: {
|
|
161
|
+
ok: true,
|
|
162
|
+
landedOn: landing.base ?? null,
|
|
163
|
+
pushed: true
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
return {
|
|
167
|
+
status: 200,
|
|
168
|
+
body: {
|
|
169
|
+
ok: true,
|
|
170
|
+
landedOn: landing.base ?? null,
|
|
171
|
+
pushed: false,
|
|
172
|
+
note: "landed locally and nothing was pushed, because the repository has no origin remote or origin did not take a fast-forward"
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function projectReviews({ configured, reason, inFlight, queued, drafts }) {
|
|
177
|
+
const identity = ({ key, title, url, author }) => ({
|
|
178
|
+
key,
|
|
179
|
+
title,
|
|
180
|
+
url,
|
|
181
|
+
author
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
ok: true,
|
|
185
|
+
configured,
|
|
186
|
+
reason: reason ?? null,
|
|
187
|
+
reviewing: inFlight.map((review) => ({
|
|
188
|
+
...identity(review),
|
|
189
|
+
phase: review.phase
|
|
190
|
+
})),
|
|
191
|
+
queued: queued.map(identity),
|
|
192
|
+
drafts: drafts.map((draft) => ({
|
|
193
|
+
...identity(draft),
|
|
194
|
+
status: draft.status,
|
|
195
|
+
verdict: draft.verdict,
|
|
196
|
+
summary: draft.summary,
|
|
197
|
+
comments: draft.comments.length,
|
|
198
|
+
isOutOfDate: draft.liveHead !== void 0 && draft.liveHead !== draft.reviewedHead
|
|
199
|
+
}))
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function projectMyPrs(snapshot) {
|
|
203
|
+
if (!snapshot) return {
|
|
204
|
+
ok: true,
|
|
205
|
+
configured: false,
|
|
206
|
+
prs: []
|
|
207
|
+
};
|
|
208
|
+
return {
|
|
209
|
+
ok: true,
|
|
210
|
+
configured: snapshot.configured,
|
|
211
|
+
prs: snapshot.prs.map((pr) => ({
|
|
212
|
+
key: pr.key,
|
|
213
|
+
title: pr.title,
|
|
214
|
+
url: pr.url,
|
|
215
|
+
state: pr.state,
|
|
216
|
+
isDraft: pr.isDraft,
|
|
217
|
+
stage: pr.stage,
|
|
218
|
+
reviewDecision: pr.reviewDecision,
|
|
219
|
+
approvals: pr.approvals,
|
|
220
|
+
unresolvedThreads: pr.unresolvedThreads,
|
|
221
|
+
behindBy: pr.behindBy,
|
|
222
|
+
mergeable: pr.mergeable,
|
|
223
|
+
isInMergeQueue: pr.isInMergeQueue,
|
|
224
|
+
checks: {
|
|
225
|
+
state: pr.checks.state,
|
|
226
|
+
failing: pr.checks.failing
|
|
227
|
+
}
|
|
228
|
+
}))
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function deriveChildSessionName(parentName, existingNames) {
|
|
232
|
+
const base = `${String(parentName || "").trim() || "session"} agent`;
|
|
233
|
+
const taken = new Set(existingNames);
|
|
234
|
+
let candidate = base;
|
|
235
|
+
let suffix = 1;
|
|
236
|
+
while (taken.has(candidate)) {
|
|
237
|
+
suffix += 1;
|
|
238
|
+
candidate = `${base} ${suffix}`;
|
|
239
|
+
}
|
|
240
|
+
return candidate;
|
|
241
|
+
}
|
|
242
|
+
var USAGE_COLUMN_WIDTH = 20;
|
|
243
|
+
var AGENT_COMMAND_HELP = Object.freeze({
|
|
244
|
+
spawn: {
|
|
245
|
+
usage: "spawn <prompt>",
|
|
246
|
+
summary: "Start a sibling session on that prompt",
|
|
247
|
+
missingArgumentHint: "glimmervoid spawn: this command needs text, for example \"glimmervoid spawn check the failing test\""
|
|
248
|
+
},
|
|
249
|
+
attention: {
|
|
250
|
+
usage: "attention <note>",
|
|
251
|
+
summary: "Flag this session as needing the operator",
|
|
252
|
+
missingArgumentHint: "glimmervoid attention: this command needs text, for example \"glimmervoid attention check the failing test\""
|
|
253
|
+
},
|
|
254
|
+
board: {
|
|
255
|
+
usage: "board",
|
|
256
|
+
summary: "List the live sessions",
|
|
257
|
+
missingArgumentHint: null
|
|
258
|
+
},
|
|
259
|
+
push: {
|
|
260
|
+
usage: "push",
|
|
261
|
+
summary: "Land this session's commits on the integration branch and push it",
|
|
262
|
+
missingArgumentHint: null
|
|
263
|
+
},
|
|
264
|
+
edit: {
|
|
265
|
+
usage: "edit <path>",
|
|
266
|
+
summary: "Open a text file for the operator to edit in the dashboard",
|
|
267
|
+
missingArgumentHint: "glimmervoid edit: this command needs a file path, for example \"glimmervoid edit drafts/post.md\""
|
|
268
|
+
},
|
|
269
|
+
review: {
|
|
270
|
+
usage: "review <pr>",
|
|
271
|
+
summary: "Queue a team review of a pull request; the draft waits in the Reviews tab for the operator to post",
|
|
272
|
+
missingArgumentHint: "glimmervoid review: this command needs a pull request, for example \"glimmervoid review https://github.com/acme/app/pull/12\" or \"glimmervoid review acme/app#12\""
|
|
273
|
+
},
|
|
274
|
+
reviews: {
|
|
275
|
+
usage: "reviews",
|
|
276
|
+
summary: "List the Reviews tab: reviews running, queued and drafted",
|
|
277
|
+
missingArgumentHint: null
|
|
278
|
+
},
|
|
279
|
+
prs: {
|
|
280
|
+
usage: "prs",
|
|
281
|
+
summary: "List the operator's open pull requests with their checks, reviews and merge state",
|
|
282
|
+
missingArgumentHint: null
|
|
283
|
+
},
|
|
284
|
+
"link-pr": {
|
|
285
|
+
usage: "link-pr <pr>",
|
|
286
|
+
summary: "Link a pull request to this session's card",
|
|
287
|
+
missingArgumentHint: "glimmervoid link-pr: this command needs a pull request, for example \"glimmervoid link-pr acme/app#12\""
|
|
288
|
+
},
|
|
289
|
+
settings: {
|
|
290
|
+
usage: "settings list|get",
|
|
291
|
+
summary: "Read Glimmervoid's settings; a secret shows only as configured or not",
|
|
292
|
+
missingArgumentHint: null
|
|
293
|
+
},
|
|
294
|
+
limits: {
|
|
295
|
+
usage: "limits",
|
|
296
|
+
summary: "List which of these commands answer here and what the guardrails block",
|
|
297
|
+
missingArgumentHint: null
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
function agentCommandHelpLines() {
|
|
301
|
+
return Object.values(AGENT_COMMAND_HELP).map(({ usage, summary }) => ` ${usage.padEnd(USAGE_COLUMN_WIDTH)}${summary}`);
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
304
|
+
export { decideSpawnAllowance as a, describeRoleRefusal as c, projectMyPrs as d, projectReviews as f, decideAgentRequest as i, isAgentApiVerb as l, REFUSAL_REASON as n, deriveChildSessionName as o, agentCommandHelpLines as r, describePushLanding as s, AGENT_COMMAND_HELP as t, parseAgentVerb as u };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as isMissingFileError, r as errorMessage } from "./text-DoirglWV.js";
|
|
2
|
-
import { a as classifyGrokHooksFile, o as renderGrokHooksFile, t as grok } from "./grok-
|
|
2
|
+
import { a as classifyGrokHooksFile, o as renderGrokHooksFile, t as grok } from "./grok-BLzhApwT.js";
|
|
3
3
|
import { p as writeTextAtomicSync } from "./json-file-DFCTvPjj.js";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { s as loadConfigFile } from "./config-store-DqMScSdg.js";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { n as errorLabel, r as errorMessage } from "./text-DoirglWV.js";
|
|
2
2
|
import { n as equalsIgnoringCaseOnWindows } from "./paths-Bu4eH1Yd.js";
|
|
3
|
+
import { $ as USAGE_BUDGET_RANGE, A as FLYING_ANIMALS_DEFAULTS, B as POSTHOG_FIX_TIMEOUT_RANGE, F as MEMORY_RESERVE_MB_RANGE, I as NON_NEGATIVE_NUMBER_RANGE, J as POSTHOG_TRAFFIC_MIN_USERS_RANGE, K as POSTHOG_TRAFFIC_BASELINE_RANGE, L as PORT_RANGE, M as FLYING_ANIMALS_GAP_RANGE, N as FLYING_ANIMALS_SCALE_RANGE, P as KEEP_MERGEABLE_TIMEOUT_MINUTES_RANGE, Q as TASK_TITLE_TIMEOUT_RANGE, R as POSITIVE_NUMBER_RANGE, Y as POSTHOG_TRAFFIC_MULTIPLIER_RANGE, Z as REPLAY_BUFFER_KB_RANGE, et as USAGE_INTEGER_RANGES, j as FLYING_ANIMALS_DURATION_RANGE, k as CHANGE_MAP_NARRATOR_TIMEOUT_RANGE, lt as WORKFLOWS_MAX_ACTIONS_PER_POLL_RANGE, nt as USAGE_SCAN_INTERVAL_RANGE, tt as USAGE_RETAIN_DAYS_RANGE, ut as WORKFLOWS_MAX_CONCURRENT_SESSIONS_RANGE } from "./control-messages-DpBaniJi.js";
|
|
3
4
|
import { n as glimmervoidHomeDir$1, t as decideConfigPath } from "./config-path-core-DFt-TemS.js";
|
|
4
5
|
import { n as isRecord } from "./coerce-NSst8oRP.js";
|
|
5
6
|
import { d as writeJsonAtomicSync, f as writeTextAtomic, p as writeTextAtomicSync } from "./json-file-DFCTvPjj.js";
|
|
6
|
-
import {
|
|
7
|
+
import { C as UPDATE_CHANNELS, E as normalizeLegacyConfigNumbers, S as POST_TURN_CHECK_MODES, _ as RUNTIME_CONFIG_SCALAR_KEYS, b as BranchGcFileSettings, d as AGENT_ID_SHAPE_MESSAGE, f as BUILTIN_AGENT_IDS, l as TELEMETRY_KILL_SWITCH_FLAG, m as Config, o as FeatureFlagsResponse, w as USAGE_COST_MODES, x as CHANGE_MAP_NARRATOR_ENGINES, y as configIssueMessage } from "./contracts-CloExMlS.js";
|
|
7
8
|
import fs from "node:fs";
|
|
8
9
|
import path from "node:path";
|
|
9
10
|
import os from "node:os";
|
|
@@ -1014,6 +1015,22 @@ var SECTIONS = Object.freeze([
|
|
|
1014
1015
|
defaultValue: false,
|
|
1015
1016
|
...MACHINE_NOW
|
|
1016
1017
|
},
|
|
1018
|
+
{
|
|
1019
|
+
id: "zen-view",
|
|
1020
|
+
path: "zenView",
|
|
1021
|
+
title: "Zen view (experimental)",
|
|
1022
|
+
description: "Add a Zen tab that shows no terminal until a session needs you, then shows only that session and what it needs.",
|
|
1023
|
+
control: "toggle",
|
|
1024
|
+
keywords: [
|
|
1025
|
+
"experimental",
|
|
1026
|
+
"calm",
|
|
1027
|
+
"focus",
|
|
1028
|
+
"attention",
|
|
1029
|
+
"needs you"
|
|
1030
|
+
],
|
|
1031
|
+
defaultValue: false,
|
|
1032
|
+
...MACHINE_NOW
|
|
1033
|
+
},
|
|
1017
1034
|
{
|
|
1018
1035
|
id: "streamer-mode",
|
|
1019
1036
|
path: "streamerMode",
|
|
@@ -3352,6 +3369,7 @@ var DEFAULT_CONFIG = {
|
|
|
3352
3369
|
debugMode: false,
|
|
3353
3370
|
conversationView: false,
|
|
3354
3371
|
streamerMode: false,
|
|
3372
|
+
zenView: false,
|
|
3355
3373
|
detectBackgroundAgents: true,
|
|
3356
3374
|
detectScheduledWakeups: true,
|
|
3357
3375
|
replayBufferKB: 512,
|
|
@@ -3788,6 +3806,7 @@ function createConfigStore({ settingsDefaults } = {}) {
|
|
|
3788
3806
|
debugMode: config.debugMode ?? effectiveDefaults.debugMode,
|
|
3789
3807
|
conversationView: config.conversationView ?? effectiveDefaults.conversationView,
|
|
3790
3808
|
streamerMode: config.streamerMode ?? effectiveDefaults.streamerMode,
|
|
3809
|
+
zenView: config.zenView ?? effectiveDefaults.zenView,
|
|
3791
3810
|
detectBackgroundAgents: config.detectBackgroundAgents ?? effectiveDefaults.detectBackgroundAgents,
|
|
3792
3811
|
recordSignals: config.recordSignals ?? effectiveDefaults.recordSignals,
|
|
3793
3812
|
trace: pickBlock("trace", {
|