gnhf 0.1.31 → 0.1.33
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 +26 -24
- package/dist/cli.mjs +139 -41
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -175,22 +175,22 @@ If you run `gnhf` on an existing `gnhf/` branch with a different prompt, gnhf as
|
|
|
175
175
|
|
|
176
176
|
### Flags
|
|
177
177
|
|
|
178
|
-
| Flag | Description
|
|
179
|
-
| ------------------------ |
|
|
180
|
-
| `--agent <agent>` | Agent to use (`claude`, `codex`, `rovodev`, `opencode`, `copilot`, `pi`, or `acp:<target>`) | config file (`claude`) |
|
|
181
|
-
| `--max-iterations <n>` | Abort after `n` total iterations
|
|
182
|
-
| `--max-tokens <n>` | Abort after `n` total input+output tokens
|
|
183
|
-
| `--stop-when <cond>` | End the loop when the agent reports this condition; persists across resume
|
|
184
|
-
| `--prevent-sleep <mode>` | Prevent system sleep during the run (`on`/`off` or `true`/`false`)
|
|
185
|
-
| `--worktree` | Run in a separate git worktree (enables multiple agents concurrently)
|
|
186
|
-
| `--version` | Show version
|
|
178
|
+
| Flag | Description | Default |
|
|
179
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------ | ---------------------- |
|
|
180
|
+
| `--agent <agent>` | Agent to use (`claude`, `codex`, `rovodev`, `opencode`, `copilot`, `pi`, or `acp:<target-or-command>`) | config file (`claude`) |
|
|
181
|
+
| `--max-iterations <n>` | Abort after `n` total iterations | unlimited |
|
|
182
|
+
| `--max-tokens <n>` | Abort after `n` total input+output tokens | unlimited |
|
|
183
|
+
| `--stop-when <cond>` | End the loop when the agent reports this condition; persists across resume | unlimited |
|
|
184
|
+
| `--prevent-sleep <mode>` | Prevent system sleep during the run (`on`/`off` or `true`/`false`) | config file (`on`) |
|
|
185
|
+
| `--worktree` | Run in a separate git worktree (enables multiple agents concurrently) | `false` |
|
|
186
|
+
| `--version` | Show version | |
|
|
187
187
|
|
|
188
188
|
## Configuration
|
|
189
189
|
|
|
190
190
|
Config lives at `~/.gnhf/config.yml`:
|
|
191
191
|
|
|
192
192
|
```yaml
|
|
193
|
-
# Agent to use by default (claude, codex, rovodev, opencode, copilot, pi, or acp:<target>)
|
|
193
|
+
# Agent to use by default (claude, codex, rovodev, opencode, copilot, pi, or acp:<target-or-command>)
|
|
194
194
|
agent: claude
|
|
195
195
|
|
|
196
196
|
# Custom paths to native agent binaries (optional)
|
|
@@ -225,7 +225,7 @@ agent: claude
|
|
|
225
225
|
# staging: "node /opt/staging/agent.mjs"
|
|
226
226
|
|
|
227
227
|
# Commit message convention (optional)
|
|
228
|
-
# Defaults to: gnhf
|
|
228
|
+
# Defaults to: gnhf <iteration>: <summary>
|
|
229
229
|
# Use the conventional preset for semantic-release compatible headers:
|
|
230
230
|
# commitMessage:
|
|
231
231
|
# preset: conventional
|
|
@@ -245,6 +245,7 @@ The iteration and token caps are runtime-only flags and are not persisted in `co
|
|
|
245
245
|
`agentArgsOverride.<name>` lets you pass through extra CLI flags for native agents (`claude`, `codex`, `rovodev`, `opencode`, `copilot`, or `pi`).
|
|
246
246
|
ACP targets do not support path or arg overrides in this version.
|
|
247
247
|
Use `acpRegistryOverrides` to map `acp:<target>` names to custom spawn commands for local, forked, or beta ACP agents.
|
|
248
|
+
You can also pass a raw custom ACP server command directly as a quoted `acp:` spec, for example `gnhf --agent 'acp:./bin/dev-acp --profile ci' "fix the tests"`.
|
|
248
249
|
|
|
249
250
|
- Use it for agent-specific options like models, profiles, or reasoning settings without adding a dedicated `gnhf` config field for each one.
|
|
250
251
|
- For `codex`, `claude`, and `copilot`, `gnhf` adds its usual non-interactive permission default only when you do not provide your own permission or execution-mode flag. If you set one explicitly, `gnhf` treats that as user-managed and does not add its default on top.
|
|
@@ -252,7 +253,7 @@ Use `acpRegistryOverrides` to map `acp:<target>` names to custom spawn commands
|
|
|
252
253
|
|
|
253
254
|
`commitMessage` controls the subject line that gnhf uses for each successful iteration commit.
|
|
254
255
|
|
|
255
|
-
- Omit it to keep the default `gnhf
|
|
256
|
+
- Omit it to keep the default `gnhf <iteration>: <summary>` format.
|
|
256
257
|
- Set `preset: conventional` to ask the agent for `type` and optional `scope`, then commit as `type(scope): summary` for semantic-release style workflows. Valid types are `build`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `test`, and `chore`; invalid or missing types fall back to `chore`, and empty scopes are omitted.
|
|
257
258
|
- The resolved commit-message convention is saved per run, so resuming a `gnhf/` branch keeps the original subject format even if `config.yml` changes later.
|
|
258
259
|
|
|
@@ -273,7 +274,8 @@ When sleep prevention is enabled, `gnhf` uses the native mechanism for your OS:
|
|
|
273
274
|
|
|
274
275
|
## Debug Logs
|
|
275
276
|
|
|
276
|
-
Every run writes a JSONL debug log to `.gnhf/runs/<runId>/gnhf.log` alongside `notes.md`. Lifecycle events for the orchestrator, agent, and HTTP requests are captured with elapsed timings and (for failures) the full `error.cause` chain
|
|
277
|
+
Every run writes a JSONL debug log to `.gnhf/runs/<runId>/gnhf.log` alongside `notes.md`. Lifecycle events for the orchestrator, agent, and HTTP requests are captured with elapsed timings and (for failures) the full `error.cause` chain, which is what you need to tell a bare `TypeError: fetch failed` apart from an undici `UND_ERR_HEADERS_TIMEOUT`. The agent's own streaming output still goes to the per-iteration `iteration-<n>.jsonl` file next to it.
|
|
278
|
+
Raw ACP command specs are redacted as `acp:custom`/`custom` in debug logs and related errors, so local paths or secrets in custom commands are not written to `gnhf.log`.
|
|
277
279
|
|
|
278
280
|
Including a snippet of `gnhf.log` is the single most useful thing you can attach when filing an issue.
|
|
279
281
|
|
|
@@ -285,17 +287,17 @@ Set `GNHF_TELEMETRY=0` to turn it off.
|
|
|
285
287
|
|
|
286
288
|
## Agents
|
|
287
289
|
|
|
288
|
-
`gnhf` supports six native agents plus ACP targets. ACP support is powered by [`acpx`](https://github.com/openclaw/acpx), which is bundled with `gnhf` and provides the runtime and agent registry for `acp:<target>` specs.
|
|
289
|
-
|
|
290
|
-
| Agent | Flag
|
|
291
|
-
| ------------------ |
|
|
292
|
-
| Claude Code | `--agent claude`
|
|
293
|
-
| Codex | `--agent codex`
|
|
294
|
-
| GitHub Copilot CLI | `--agent copilot`
|
|
295
|
-
| Pi | `--agent pi`
|
|
296
|
-
| Rovo Dev | `--agent rovodev`
|
|
297
|
-
| OpenCode | `--agent opencode`
|
|
298
|
-
| ACP target | `--agent acp:<target>` | Install and authenticate the target supported by the bundled [`acpx`](https://github.com/openclaw/acpx) registry, such as `acp:gemini
|
|
290
|
+
`gnhf` supports six native agents plus ACP targets. ACP support is powered by [`acpx`](https://github.com/openclaw/acpx), which is bundled with `gnhf` and provides the runtime and agent registry for `acp:<target-or-command>` specs.
|
|
291
|
+
|
|
292
|
+
| Agent | Flag | Requirements | Notes |
|
|
293
|
+
| ------------------ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
294
|
+
| Claude Code | `--agent claude` | Install Anthropic's `claude` CLI and sign in first. | `gnhf` invokes `claude` directly in non-interactive mode. After Claude emits a successful structured result, `gnhf` treats that result as final and shuts down any lingering Claude process tree after a short grace period. |
|
|
295
|
+
| Codex | `--agent codex` | Install OpenAI's `codex` CLI and sign in first. | `gnhf` invokes `codex exec` directly in non-interactive mode. |
|
|
296
|
+
| GitHub Copilot CLI | `--agent copilot` | Install GitHub Copilot CLI and sign in first. | `gnhf` invokes `copilot` directly in non-interactive JSONL mode. Copilot currently exposes assistant output tokens, but not full input/cache token totals; see https://github.com/github/copilot-cli/issues/1152. |
|
|
297
|
+
| Pi | `--agent pi` | Install the `pi` CLI and configure a usable provider/model first. | `gnhf` invokes `pi` directly in JSON mode, appends the final output schema to the prompt, and disables Pi session persistence with `--no-session`. |
|
|
298
|
+
| Rovo Dev | `--agent rovodev` | Install Atlassian's `acli` and authenticate it with Rovo Dev first. | `gnhf` starts a local `acli rovodev serve --disable-session-token <port>` process automatically in the repo workspace. |
|
|
299
|
+
| OpenCode | `--agent opencode` | Install `opencode` and configure at least one usable model provider first. | `gnhf` starts a local `opencode serve --hostname 127.0.0.1 --port <port> --print-logs` process automatically, creates a per-run session, and applies a blanket allow rule so tool calls do not block on prompts. |
|
|
300
|
+
| ACP target | `--agent acp:<target-or-command>` | Install and authenticate the target supported by the bundled [`acpx`](https://github.com/openclaw/acpx) registry, such as `acp:gemini`, or pass a quoted custom ACP server command. | `gnhf` runs the target through ACP with a persistent per-run session under `.gnhf/runs/<runId>/acp-sessions`; token usage and `--max-tokens` use ACP `used` deltas when available, with prompt-length plus tool-call estimates as a fallback, and `agentPathOverride` and `agentArgsOverride` do not apply. |
|
|
299
301
|
|
|
300
302
|
## Development
|
|
301
303
|
|
package/dist/cli.mjs
CHANGED
|
@@ -37,12 +37,21 @@ const AGENT_NAMES = [
|
|
|
37
37
|
"copilot",
|
|
38
38
|
"pi"
|
|
39
39
|
];
|
|
40
|
-
const ACP_SPEC_PATTERN = /^acp:[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
41
40
|
function isAgentName$1(name) {
|
|
42
|
-
return AGENT_NAMES.includes(name);
|
|
41
|
+
return typeof name === "string" && AGENT_NAMES.includes(name);
|
|
42
|
+
}
|
|
43
|
+
function hasDisallowedAcpTargetChar(target) {
|
|
44
|
+
for (let i = 0; i < target.length; i += 1) {
|
|
45
|
+
const code = target.charCodeAt(i);
|
|
46
|
+
if (code < 32 || code === 127) return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
43
49
|
}
|
|
44
50
|
function isAcpSpec(spec) {
|
|
45
|
-
|
|
51
|
+
if (typeof spec !== "string") return false;
|
|
52
|
+
if (!spec.startsWith("acp:")) return false;
|
|
53
|
+
const target = spec.slice(4);
|
|
54
|
+
return target.length > 0 && target.trim() === target && !hasDisallowedAcpTargetChar(target);
|
|
46
55
|
}
|
|
47
56
|
function isAgentSpec(spec) {
|
|
48
57
|
return isAgentName$1(spec) || isAcpSpec(spec);
|
|
@@ -50,6 +59,16 @@ function isAgentSpec(spec) {
|
|
|
50
59
|
function getAcpTarget(spec) {
|
|
51
60
|
return spec.slice(4);
|
|
52
61
|
}
|
|
62
|
+
function isNamedAcpTarget(target) {
|
|
63
|
+
return ACP_TARGET_NAME_PATTERN.test(target);
|
|
64
|
+
}
|
|
65
|
+
function redactAcpTargetForLogs(target) {
|
|
66
|
+
return isNamedAcpTarget(target) ? target : "custom";
|
|
67
|
+
}
|
|
68
|
+
function redactAgentSpecForLogs(spec) {
|
|
69
|
+
if (!spec.startsWith("acp:")) return spec;
|
|
70
|
+
return `acp:${redactAcpTargetForLogs(spec.slice(4))}`;
|
|
71
|
+
}
|
|
53
72
|
const DEFAULT_CONFIG = {
|
|
54
73
|
agent: "claude",
|
|
55
74
|
agentPathOverride: {},
|
|
@@ -199,12 +218,19 @@ function serializeAgentArgsOverride(agentArgsOverride) {
|
|
|
199
218
|
sortKeys: false
|
|
200
219
|
}).trimEnd();
|
|
201
220
|
}
|
|
221
|
+
function serializeAgent(agent) {
|
|
222
|
+
return yaml.dump({ agent }, {
|
|
223
|
+
lineWidth: -1,
|
|
224
|
+
noRefs: true,
|
|
225
|
+
sortKeys: false
|
|
226
|
+
}).trimEnd();
|
|
227
|
+
}
|
|
202
228
|
function serializeConfig(config) {
|
|
203
229
|
const agentPathOverrideSection = serializeAgentPathOverride(config.agentPathOverride);
|
|
204
230
|
const agentArgsOverrideSection = serializeAgentArgsOverride(config.agentArgsOverride);
|
|
205
231
|
const lines = [
|
|
206
|
-
"# Agent to use by default: native agent name or acp:<target>",
|
|
207
|
-
|
|
232
|
+
"# Agent to use by default: native agent name or acp:<target-or-command>",
|
|
233
|
+
serializeAgent(config.agent),
|
|
208
234
|
"",
|
|
209
235
|
"# Custom paths to native agent binaries (optional)",
|
|
210
236
|
"# Paths may be absolute, bare executable names on PATH,",
|
|
@@ -237,14 +263,14 @@ function serializeConfig(config) {
|
|
|
237
263
|
"# - high",
|
|
238
264
|
"",
|
|
239
265
|
"# Custom ACP target commands (optional)",
|
|
240
|
-
"# Maps acp:<target> names to spawn commands. Useful for
|
|
266
|
+
"# Maps acp:<target> names to spawn commands. Useful for naming a",
|
|
241
267
|
"# local or beta build of an ACP agent.",
|
|
242
268
|
"# acpRegistryOverrides:",
|
|
243
269
|
"# my-fork: \"/usr/local/bin/my-claude-code-fork --acp\"",
|
|
244
270
|
"# staging: \"node /opt/staging/agent.mjs\"",
|
|
245
271
|
"",
|
|
246
272
|
"# Commit message convention (optional)",
|
|
247
|
-
"# Defaults to: gnhf
|
|
273
|
+
"# Defaults to: gnhf <iteration>: <summary>",
|
|
248
274
|
"# Use Conventional Commits semantic-release headers:",
|
|
249
275
|
"# commitMessage:",
|
|
250
276
|
"# preset: conventional"
|
|
@@ -660,7 +686,7 @@ function resolveConventionalScope(value) {
|
|
|
660
686
|
return scope === "" ? "" : `(${scope})`;
|
|
661
687
|
}
|
|
662
688
|
function buildCommitMessage(config, output, context) {
|
|
663
|
-
if (config === void 0) return collapseHeader(`gnhf
|
|
689
|
+
if (config === void 0) return collapseHeader(`gnhf ${context.iteration}: ${output.summary}`);
|
|
664
690
|
const commitOutput = output;
|
|
665
691
|
return collapseHeader(`${resolveConventionalType(commitOutput.type)}${resolveConventionalScope(commitOutput.scope)}: ${output.summary}`);
|
|
666
692
|
}
|
|
@@ -13461,6 +13487,40 @@ function isAbortError$2(error) {
|
|
|
13461
13487
|
function createAbortError$2() {
|
|
13462
13488
|
return /* @__PURE__ */ new Error("Agent was aborted");
|
|
13463
13489
|
}
|
|
13490
|
+
function redactRawAcpTargetInString(text, target) {
|
|
13491
|
+
const redacted = redactAcpTargetForLogs(target);
|
|
13492
|
+
if (redacted === target) return text;
|
|
13493
|
+
return text.split(target).join(redacted);
|
|
13494
|
+
}
|
|
13495
|
+
function redactRawAcpTargetInValue(value, target) {
|
|
13496
|
+
if (typeof value === "string") return redactRawAcpTargetInString(value, target);
|
|
13497
|
+
if (Array.isArray(value)) return value.map((item) => redactRawAcpTargetInValue(item, target));
|
|
13498
|
+
if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, redactRawAcpTargetInValue(entry, target)]));
|
|
13499
|
+
return value;
|
|
13500
|
+
}
|
|
13501
|
+
function serializeAcpErrorForLog(error, target) {
|
|
13502
|
+
return redactRawAcpTargetInValue(serializeError(error), target);
|
|
13503
|
+
}
|
|
13504
|
+
function redactAcpErrorForThrow(error, target) {
|
|
13505
|
+
if (redactAcpTargetForLogs(target) === target) return error;
|
|
13506
|
+
if (error instanceof PermanentAgentError) return new PermanentAgentError(redactRawAcpTargetInString(error.message, target), redactRawAcpTargetInString(error.detail, target));
|
|
13507
|
+
if (error instanceof Error) {
|
|
13508
|
+
let cause;
|
|
13509
|
+
try {
|
|
13510
|
+
cause = "cause" in error ? error.cause : void 0;
|
|
13511
|
+
} catch {
|
|
13512
|
+
cause = void 0;
|
|
13513
|
+
}
|
|
13514
|
+
const redactedCause = cause === void 0 ? void 0 : redactAcpErrorForThrow(cause, target);
|
|
13515
|
+
const redactedError = new Error(redactRawAcpTargetInString(error.message, target), redactedCause === void 0 ? void 0 : { cause: redactedCause });
|
|
13516
|
+
redactedError.name = error.name;
|
|
13517
|
+
if (typeof error.stack === "string") redactedError.stack = redactRawAcpTargetInString(error.stack, target);
|
|
13518
|
+
const code = error.code;
|
|
13519
|
+
if (code !== void 0) redactedError.code = code;
|
|
13520
|
+
return redactedError;
|
|
13521
|
+
}
|
|
13522
|
+
return redactRawAcpTargetInValue(error, target);
|
|
13523
|
+
}
|
|
13464
13524
|
function estimateTokens(charCount) {
|
|
13465
13525
|
if (charCount <= 0) return 0;
|
|
13466
13526
|
return Math.ceil(charCount / 4);
|
|
@@ -13493,30 +13553,47 @@ var AcpAgent = class {
|
|
|
13493
13553
|
const { signal, onMessage, onUsage, logPath } = options ?? {};
|
|
13494
13554
|
if (signal?.aborted) throw createAbortError$2();
|
|
13495
13555
|
const runtime = this.ensureRuntime(cwd);
|
|
13496
|
-
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
|
|
13501
|
-
|
|
13556
|
+
let handle;
|
|
13557
|
+
try {
|
|
13558
|
+
handle = await runtime.ensureSession({
|
|
13559
|
+
sessionKey: this.runId,
|
|
13560
|
+
agent: this.target,
|
|
13561
|
+
mode: "persistent",
|
|
13562
|
+
cwd
|
|
13563
|
+
});
|
|
13564
|
+
} catch (error) {
|
|
13565
|
+
throw redactAcpErrorForThrow(error, this.target);
|
|
13566
|
+
}
|
|
13502
13567
|
this.handle = handle;
|
|
13503
13568
|
const requestId = randomUUID();
|
|
13504
13569
|
appendDebugLog("acp:turn:start", {
|
|
13505
|
-
target: this.target,
|
|
13570
|
+
target: redactAcpTargetForLogs(this.target),
|
|
13506
13571
|
sessionKey: this.runId,
|
|
13507
13572
|
requestId,
|
|
13508
13573
|
cwd
|
|
13509
13574
|
});
|
|
13510
13575
|
const acpPrompt = buildAcpPrompt(prompt, this.schema);
|
|
13511
13576
|
const promptTokenEstimate = estimateTokens(acpPrompt.length);
|
|
13512
|
-
const turn = runtime.startTurn({
|
|
13513
|
-
handle,
|
|
13514
|
-
text: acpPrompt,
|
|
13515
|
-
mode: "prompt",
|
|
13516
|
-
requestId,
|
|
13517
|
-
signal
|
|
13518
|
-
});
|
|
13519
13577
|
const startedAt = Date.now();
|
|
13578
|
+
const turn = (() => {
|
|
13579
|
+
try {
|
|
13580
|
+
return runtime.startTurn({
|
|
13581
|
+
handle,
|
|
13582
|
+
text: acpPrompt,
|
|
13583
|
+
mode: "prompt",
|
|
13584
|
+
requestId,
|
|
13585
|
+
signal
|
|
13586
|
+
});
|
|
13587
|
+
} catch (error) {
|
|
13588
|
+
appendDebugLog("acp:turn:start-error", {
|
|
13589
|
+
target: redactAcpTargetForLogs(this.target),
|
|
13590
|
+
requestId,
|
|
13591
|
+
elapsedMs: Date.now() - startedAt,
|
|
13592
|
+
error: serializeAcpErrorForLog(error, this.target)
|
|
13593
|
+
});
|
|
13594
|
+
throw redactAcpErrorForThrow(error, this.target);
|
|
13595
|
+
}
|
|
13596
|
+
})();
|
|
13520
13597
|
const iterationStartUsed = this.lastReportedUsed;
|
|
13521
13598
|
let latestUsed = iterationStartUsed;
|
|
13522
13599
|
let usageUpdateReceived = iterationStartUsed > 0;
|
|
@@ -13587,23 +13664,23 @@ var AcpAgent = class {
|
|
|
13587
13664
|
if (signal?.aborted || isAbortError$2(error)) {
|
|
13588
13665
|
await turn.cancel({ reason: "gnhf-aborted" }).catch(() => void 0);
|
|
13589
13666
|
appendDebugLog("acp:turn:aborted", {
|
|
13590
|
-
target: this.target,
|
|
13667
|
+
target: redactAcpTargetForLogs(this.target),
|
|
13591
13668
|
requestId,
|
|
13592
13669
|
elapsedMs: Date.now() - startedAt
|
|
13593
13670
|
});
|
|
13594
13671
|
throw createAbortError$2();
|
|
13595
13672
|
}
|
|
13596
13673
|
appendDebugLog("acp:turn:stream-error", {
|
|
13597
|
-
target: this.target,
|
|
13674
|
+
target: redactAcpTargetForLogs(this.target),
|
|
13598
13675
|
requestId,
|
|
13599
13676
|
elapsedMs: Date.now() - startedAt,
|
|
13600
|
-
error:
|
|
13677
|
+
error: serializeAcpErrorForLog(error, this.target)
|
|
13601
13678
|
});
|
|
13602
|
-
throw error;
|
|
13679
|
+
throw redactAcpErrorForThrow(error, this.target);
|
|
13603
13680
|
}
|
|
13604
13681
|
const result = await turn.result;
|
|
13605
13682
|
appendDebugLog("acp:turn:result", {
|
|
13606
|
-
target: this.target,
|
|
13683
|
+
target: redactAcpTargetForLogs(this.target),
|
|
13607
13684
|
requestId,
|
|
13608
13685
|
status: result.status,
|
|
13609
13686
|
stopReason: result.status === "completed" || result.status === "cancelled" ? result.stopReason : void 0,
|
|
@@ -13614,7 +13691,7 @@ var AcpAgent = class {
|
|
|
13614
13691
|
});
|
|
13615
13692
|
if (result.status === "cancelled") throw createAbortError$2();
|
|
13616
13693
|
if (result.status === "failed") {
|
|
13617
|
-
const message = result.error.message || "ACP turn failed";
|
|
13694
|
+
const message = redactRawAcpTargetInString(result.error.message || "ACP turn failed", this.target);
|
|
13618
13695
|
if (result.error.retryable === false) throw new PermanentAgentError(message, result.error.code ?? "ACP_TURN_FAILED");
|
|
13619
13696
|
throw new Error(message);
|
|
13620
13697
|
}
|
|
@@ -13658,11 +13735,11 @@ var AcpAgent = class {
|
|
|
13658
13735
|
handle,
|
|
13659
13736
|
reason: "gnhf-shutdown"
|
|
13660
13737
|
});
|
|
13661
|
-
appendDebugLog("acp:close", { target: this.target });
|
|
13738
|
+
appendDebugLog("acp:close", { target: redactAcpTargetForLogs(this.target) });
|
|
13662
13739
|
} catch (error) {
|
|
13663
13740
|
appendDebugLog("acp:close-error", {
|
|
13664
|
-
target: this.target,
|
|
13665
|
-
error:
|
|
13741
|
+
target: redactAcpTargetForLogs(this.target),
|
|
13742
|
+
error: serializeAcpErrorForLog(error, this.target)
|
|
13666
13743
|
});
|
|
13667
13744
|
}
|
|
13668
13745
|
}
|
|
@@ -13677,7 +13754,7 @@ var AcpAgent = class {
|
|
|
13677
13754
|
});
|
|
13678
13755
|
this.runtime = runtime;
|
|
13679
13756
|
appendDebugLog("acp:runtime:created", {
|
|
13680
|
-
target: this.target,
|
|
13757
|
+
target: redactAcpTargetForLogs(this.target),
|
|
13681
13758
|
sessionStateDir: this.sessionStateDir,
|
|
13682
13759
|
cwd
|
|
13683
13760
|
});
|
|
@@ -16039,7 +16116,7 @@ function getBuildTimeUmamiHost() {
|
|
|
16039
16116
|
return "https://a.kunchenguid.com";
|
|
16040
16117
|
}
|
|
16041
16118
|
function getBuildTimeUmamiWebsiteID() {
|
|
16042
|
-
return "";
|
|
16119
|
+
return "7f4707fa-4e80-4977-8320-4ade27692d85";
|
|
16043
16120
|
}
|
|
16044
16121
|
function normalizeEndpoint(host) {
|
|
16045
16122
|
let url;
|
|
@@ -16343,7 +16420,7 @@ var Orchestrator = class extends EventEmitter {
|
|
|
16343
16420
|
this.state.status = "running";
|
|
16344
16421
|
this.emit("state", this.getState());
|
|
16345
16422
|
appendDebugLog("orchestrator:start", {
|
|
16346
|
-
agent: this.agent.name,
|
|
16423
|
+
agent: redactAgentSpecForLogs(this.agent.name),
|
|
16347
16424
|
runId: this.runInfo.runId,
|
|
16348
16425
|
startIteration: this.state.currentIteration,
|
|
16349
16426
|
maxIterations: this.limits.maxIterations,
|
|
@@ -16502,7 +16579,7 @@ var Orchestrator = class extends EventEmitter {
|
|
|
16502
16579
|
const agentStartedAt = Date.now();
|
|
16503
16580
|
appendDebugLog("agent:run:start", {
|
|
16504
16581
|
iteration: this.state.currentIteration,
|
|
16505
|
-
agent: this.agent.name,
|
|
16582
|
+
agent: redactAgentSpecForLogs(this.agent.name),
|
|
16506
16583
|
logPath
|
|
16507
16584
|
});
|
|
16508
16585
|
try {
|
|
@@ -17548,7 +17625,7 @@ const GNHF_REEXEC_STDIN_PROMPT_FILE = "GNHF_REEXEC_STDIN_PROMPT_FILE";
|
|
|
17548
17625
|
const GNHF_REEXEC_STDIN_PROMPT_DIR_PREFIX = "gnhf-stdin-";
|
|
17549
17626
|
const GNHF_REEXEC_STDIN_PROMPT_FILENAME = "prompt.txt";
|
|
17550
17627
|
const AGENT_NAME_SET = new Set(AGENT_NAMES);
|
|
17551
|
-
const AGENT_SPEC_LIST = `${`"${AGENT_NAMES.slice(0, -1).join("\", \"")}", or "${AGENT_NAMES[AGENT_NAMES.length - 1]}"`}, or "acp:<target>" (e.g. acp:gemini)`;
|
|
17628
|
+
const AGENT_SPEC_LIST = `${`"${AGENT_NAMES.slice(0, -1).join("\", \"")}", or "${AGENT_NAMES[AGENT_NAMES.length - 1]}"`}, or "acp:<target-or-command>" (e.g. acp:gemini)`;
|
|
17552
17629
|
var PromptSignalError = class extends Error {
|
|
17553
17630
|
constructor(signal) {
|
|
17554
17631
|
super(signal);
|
|
@@ -17576,6 +17653,26 @@ function isAgentName(name) {
|
|
|
17576
17653
|
function getNativeAgentName(spec) {
|
|
17577
17654
|
return isAgentName(spec) ? spec : void 0;
|
|
17578
17655
|
}
|
|
17656
|
+
function getTelemetryAgent(spec) {
|
|
17657
|
+
return redactAgentSpecForLogs(spec);
|
|
17658
|
+
}
|
|
17659
|
+
function redactDebugArgs(args) {
|
|
17660
|
+
const redacted = [...args];
|
|
17661
|
+
for (let i = 0; i < redacted.length; i += 1) {
|
|
17662
|
+
const arg = redacted[i];
|
|
17663
|
+
if (arg === "--") break;
|
|
17664
|
+
if (arg === "--agent") {
|
|
17665
|
+
const next = redacted[i + 1];
|
|
17666
|
+
if (next !== void 0) {
|
|
17667
|
+
redacted[i + 1] = redactAgentSpecForLogs(next);
|
|
17668
|
+
i += 1;
|
|
17669
|
+
}
|
|
17670
|
+
continue;
|
|
17671
|
+
}
|
|
17672
|
+
if (arg?.startsWith("--agent=")) redacted[i] = `--agent=${redactAgentSpecForLogs(arg.slice(8))}`;
|
|
17673
|
+
}
|
|
17674
|
+
return redacted;
|
|
17675
|
+
}
|
|
17579
17676
|
function buildSchemaOptions(stopWhen, commitMessage) {
|
|
17580
17677
|
const commitFields = getCommitMessageSchemaFields(commitMessage);
|
|
17581
17678
|
return {
|
|
@@ -17797,7 +17894,7 @@ function readReexecStdinPrompt(env) {
|
|
|
17797
17894
|
}
|
|
17798
17895
|
}
|
|
17799
17896
|
const program = new Command();
|
|
17800
|
-
program.name("gnhf").description("Before I go to bed, I tell my agents: good night, have fun").version(packageVersion).argument("[prompt]", "The objective for the coding agent").option("--agent <agent>", `Agent to use (${AGENT_NAMES.join(", ")}, or acp:<target>)`).option("--max-iterations <n>", "Abort after N total iterations", parseNonNegativeInteger).option("--max-tokens <n>", "Abort after N total input+output tokens", parseNonNegativeInteger).option("--stop-when <condition>", "End when the agent reports this condition; resumes reuse it, pass a new value to overwrite or \"\" to clear").option("--prevent-sleep <mode>", "Prevent system sleep during the run (\"on\" or \"off\")", parseOnOffBoolean).option("--worktree", "Run in a separate git worktree (enables multiple agents on the same repo)", false).option("--mock", "", false).action(async (promptArg, options) => {
|
|
17897
|
+
program.name("gnhf").description("Before I go to bed, I tell my agents: good night, have fun").version(packageVersion).argument("[prompt]", "The objective for the coding agent").option("--agent <agent>", `Agent to use (${AGENT_NAMES.join(", ")}, or acp:<target-or-command>)`).option("--max-iterations <n>", "Abort after N total iterations", parseNonNegativeInteger).option("--max-tokens <n>", "Abort after N total input+output tokens", parseNonNegativeInteger).option("--stop-when <condition>", "End when the agent reports this condition; resumes reuse it, pass a new value to overwrite or \"\" to clear").option("--prevent-sleep <mode>", "Prevent system sleep during the run (\"on\" or \"off\")", parseOnOffBoolean).option("--worktree", "Run in a separate git worktree (enables multiple agents on the same repo)", false).option("--mock", "", false).action(async (promptArg, options) => {
|
|
17801
17898
|
if (options.mock) {
|
|
17802
17899
|
const mock = new MockOrchestrator();
|
|
17803
17900
|
enterAltScreen();
|
|
@@ -17939,16 +18036,17 @@ program.name("gnhf").description("Before I go to bed, I tell my agents: good nig
|
|
|
17939
18036
|
}
|
|
17940
18037
|
}
|
|
17941
18038
|
const runMode = options.worktree ? "worktree" : startIteration > 0 ? "resume" : "new";
|
|
18039
|
+
const telemetryAgent = getTelemetryAgent(config.agent);
|
|
17942
18040
|
telemetry.pageview("/run", {
|
|
17943
|
-
agent:
|
|
18041
|
+
agent: telemetryAgent,
|
|
17944
18042
|
mode: runMode
|
|
17945
18043
|
});
|
|
17946
18044
|
initDebugLog(runInfo.logPath);
|
|
17947
18045
|
appendDebugLog("run:start", {
|
|
17948
|
-
args: process$1.argv.slice(2),
|
|
18046
|
+
args: redactDebugArgs(process$1.argv.slice(2)),
|
|
17949
18047
|
runId: runInfo.runId,
|
|
17950
18048
|
runDir: runInfo.runDir,
|
|
17951
|
-
agent: config.agent,
|
|
18049
|
+
agent: redactAgentSpecForLogs(config.agent),
|
|
17952
18050
|
promptLength: prompt.length,
|
|
17953
18051
|
promptFromStdin,
|
|
17954
18052
|
startIteration,
|
|
@@ -18049,7 +18147,7 @@ program.name("gnhf").description("Before I go to bed, I tell my agents: good nig
|
|
|
18049
18147
|
worktreePath
|
|
18050
18148
|
});
|
|
18051
18149
|
telemetry.track("run", {
|
|
18052
|
-
agent:
|
|
18150
|
+
agent: telemetryAgent,
|
|
18053
18151
|
mode: runMode,
|
|
18054
18152
|
status: finalState.status,
|
|
18055
18153
|
signal: shutdownSignal ?? void 0,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gnhf",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
4
|
"description": "Before I go to bed, I tell my agents: good night, have fun",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"@types/js-yaml": "^4.0.9",
|
|
28
28
|
"@types/node": "^22.0.0",
|
|
29
29
|
"@vitest/coverage-v8": "^4.1.2",
|
|
30
|
+
"acp-mock": "^1.1.0",
|
|
30
31
|
"acpx": "^0.6.1",
|
|
31
32
|
"eslint": "^9.0.0",
|
|
32
33
|
"eslint-config-prettier": "^10.0.0",
|