harnery 0.46.0 → 0.47.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 +24 -0
- package/bin/README.md +10 -0
- package/bin/agent-coord.mjs +3 -0
- package/bin/agent-hook.mjs +3 -0
- package/bin/harn.mjs +3 -0
- package/bin/run.mjs +43 -0
- package/dist/commander.d.ts +18 -6
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +74 -2
- package/dist/commands/agents.d.ts +25 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +70 -27
- package/dist/commands/autoupdate.d.ts +12 -0
- package/dist/commands/autoupdate.d.ts.map +1 -0
- package/dist/commands/autoupdate.js +190 -0
- package/dist/commands/computer.d.ts +45 -0
- package/dist/commands/computer.d.ts.map +1 -0
- package/dist/commands/computer.js +509 -0
- package/dist/commands/docs.js +3 -1
- package/dist/commands/fetch.js +11 -1
- package/dist/commands/grep.d.ts +72 -0
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +200 -47
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +15 -9
- package/dist/commands/rm.js +2 -2
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +2 -0
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +2 -3
- package/dist/core/agents/coord-bin.d.ts +3 -0
- package/dist/core/agents/coord-bin.d.ts.map +1 -1
- package/dist/core/agents/coord-bin.js +6 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +6 -2
- package/dist/core/agents/render/prompt-context.d.ts +55 -7
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +122 -48
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +36 -16
- package/dist/core/agents/rules/stop-hook.d.ts +6 -0
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +20 -1
- package/dist/core/agents/state/proc-start.d.ts +3 -1
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +26 -4
- package/dist/core/autoupdate/engine.d.ts +97 -0
- package/dist/core/autoupdate/engine.d.ts.map +1 -0
- package/dist/core/autoupdate/engine.js +548 -0
- package/dist/core/autoupdate/fingerprint.d.ts +16 -0
- package/dist/core/autoupdate/fingerprint.d.ts.map +1 -0
- package/dist/core/autoupdate/fingerprint.js +73 -0
- package/dist/core/autoupdate/git.d.ts +30 -0
- package/dist/core/autoupdate/git.d.ts.map +1 -0
- package/dist/core/autoupdate/git.js +82 -0
- package/dist/core/autoupdate/index.d.ts +6 -0
- package/dist/core/autoupdate/index.d.ts.map +1 -0
- package/dist/core/autoupdate/index.js +4 -0
- package/dist/core/autoupdate/state.d.ts +59 -0
- package/dist/core/autoupdate/state.d.ts.map +1 -0
- package/dist/core/autoupdate/state.js +214 -0
- package/dist/core/autoupdate/types.d.ts +113 -0
- package/dist/core/autoupdate/types.d.ts.map +1 -0
- package/dist/core/autoupdate/types.js +35 -0
- package/dist/core/config.d.ts +53 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +114 -1
- package/dist/core/events/v3/bootstrap.js +1 -0
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +1 -0
- package/dist/core/hooks/adapter/wiring.d.ts +12 -2
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +38 -8
- package/dist/core/hooks/autoupdate-cue.d.ts +29 -0
- package/dist/core/hooks/autoupdate-cue.d.ts.map +1 -0
- package/dist/core/hooks/autoupdate-cue.js +53 -0
- package/dist/core/hooks/cli.js +52 -4
- package/dist/core/hooks/effects/index.js +4 -4
- package/dist/core/hooks/resolve/coord-root.js +1 -0
- package/dist/core/hooks/resolve/transcript.d.ts +12 -0
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +54 -0
- package/dist/core/semantic/contract.d.ts +3 -3
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +4 -2
- package/dist/core/storage/modes.d.ts +1 -1
- package/dist/core/storage/modes.d.ts.map +1 -1
- package/dist/core/storage/modes.js +5 -1
- package/dist/core/workflow/durable-record.d.ts +2 -0
- package/dist/core/workflow/durable-record.d.ts.map +1 -1
- package/dist/core/workflow/durable-record.js +13 -1
- package/dist/core/workflow/engine.js +4 -0
- package/dist/core/workflow/spawn-claude.d.ts +7 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +16 -4
- package/dist/core/workflow/specialists.d.ts.map +1 -1
- package/dist/core/workflow/specialists.js +1 -0
- package/dist/core/workflow/types.d.ts +10 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +1 -9
- package/dist/lib/computer/cua.d.ts +50 -0
- package/dist/lib/computer/cua.d.ts.map +1 -0
- package/dist/lib/computer/cua.js +474 -0
- package/dist/lib/computer/host.d.ts +19 -0
- package/dist/lib/computer/host.d.ts.map +1 -0
- package/dist/lib/computer/host.js +72 -0
- package/dist/lib/computer/lease.d.ts +13 -0
- package/dist/lib/computer/lease.d.ts.map +1 -0
- package/dist/lib/computer/lease.js +269 -0
- package/dist/lib/computer/types.d.ts +163 -0
- package/dist/lib/computer/types.d.ts.map +1 -0
- package/dist/lib/computer/types.js +10 -0
- package/dist/lib/docs-metadata-v2.d.ts.map +1 -1
- package/dist/lib/docs-metadata-v2.js +6 -5
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +1 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -1
- package/dist/lib/guarded-remove.js +5 -4
- package/dist/lib/headless/index.d.ts +33 -0
- package/dist/lib/headless/index.d.ts.map +1 -1
- package/dist/lib/headless/index.js +38 -2
- package/dist/lib/http/client.d.ts +2 -9
- package/dist/lib/http/client.d.ts.map +1 -1
- package/dist/lib/http/client.js +30 -9
- package/dist/lib/instructions/apply.d.ts +9 -0
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +92 -14
- package/dist/lib/instructions/git-hooks.d.ts.map +1 -1
- package/dist/lib/instructions/git-hooks.js +8 -4
- package/dist/lib/instructions/templates.d.ts +11 -0
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +81 -1
- package/package.json +5 -5
- package/schemas/config.schema.json +89 -1
- package/src/commander.ts +98 -8
- package/src/commands/agents.ts +100 -32
- package/src/commands/autoupdate.ts +250 -0
- package/src/commands/computer.ts +682 -0
- package/src/commands/docs.ts +3 -1
- package/src/commands/fetch.ts +11 -1
- package/src/commands/grep.ts +274 -59
- package/src/commands/init.ts +17 -7
- package/src/commands/rm.ts +2 -2
- package/src/commands/web.ts +2 -0
- package/src/core/agents/canonical-emit.ts +2 -3
- package/src/core/agents/coord-bin.ts +30 -0
- package/src/core/agents/coord-client.ts +6 -2
- package/src/core/agents/render/prompt-context.ts +152 -53
- package/src/core/agents/rules/claim-conflict.ts +50 -18
- package/src/core/agents/rules/stop-hook.ts +28 -1
- package/src/core/agents/state/proc-start.ts +29 -5
- package/src/core/autoupdate/engine.ts +691 -0
- package/src/core/autoupdate/fingerprint.ts +89 -0
- package/src/core/autoupdate/git.ts +120 -0
- package/src/core/autoupdate/index.ts +29 -0
- package/src/core/autoupdate/state.ts +269 -0
- package/src/core/autoupdate/types.ts +135 -0
- package/src/core/config.ts +142 -1
- package/src/core/events/v3/bootstrap.ts +1 -0
- package/src/core/events/v3/runtime-build.ts +1 -0
- package/src/core/hooks/adapter/wiring.ts +51 -6
- package/src/core/hooks/autoupdate-cue.ts +72 -0
- package/src/core/hooks/cli.ts +54 -1
- package/src/core/hooks/effects/index.ts +4 -4
- package/src/core/hooks/resolve/coord-root.ts +1 -0
- package/src/core/hooks/resolve/transcript.ts +61 -0
- package/src/core/semantic/service.ts +6 -2
- package/src/core/storage/modes.ts +7 -1
- package/src/core/workflow/durable-record.ts +15 -1
- package/src/core/workflow/engine.ts +3 -0
- package/src/core/workflow/spawn-claude.ts +16 -4
- package/src/core/workflow/specialists.ts +1 -0
- package/src/core/workflow/types.ts +10 -0
- package/src/core/workflow/workspaces/leases.ts +1 -9
- package/src/lib/computer/cua.ts +572 -0
- package/src/lib/computer/host.ts +87 -0
- package/src/lib/computer/lease.ts +299 -0
- package/src/lib/computer/types.ts +177 -0
- package/src/lib/docs-metadata-v2.ts +24 -5
- package/src/lib/exec.ts +1 -0
- package/src/lib/guarded-remove.ts +5 -3
- package/src/lib/headless/index.ts +66 -2
- package/src/lib/http/client.ts +33 -19
- package/src/lib/instructions/apply.ts +100 -12
- package/src/lib/instructions/git-hooks.ts +8 -4
- package/src/lib/instructions/templates.ts +95 -1
|
@@ -308,6 +308,89 @@
|
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
},
|
|
311
|
+
"computer": {
|
|
312
|
+
"type": "object",
|
|
313
|
+
"additionalProperties": false,
|
|
314
|
+
"description": "Opt-in desktop control through a pinned desktop driver (ADR 0228). Project config only; inert unless enabled is true.",
|
|
315
|
+
"properties": {
|
|
316
|
+
"enabled": {
|
|
317
|
+
"type": "boolean",
|
|
318
|
+
"default": false,
|
|
319
|
+
"description": "Allow `computer install`, `up`, `call`, and `lease acquire` in this project, and render the harn-computer skill on init."
|
|
320
|
+
},
|
|
321
|
+
"idle_seconds": {
|
|
322
|
+
"type": "number",
|
|
323
|
+
"exclusiveMinimum": 0,
|
|
324
|
+
"maximum": 86400,
|
|
325
|
+
"default": 120,
|
|
326
|
+
"description": "Desktop lease idle expiry. Every `computer call` by the holder refreshes it."
|
|
327
|
+
},
|
|
328
|
+
"wait_seconds": {
|
|
329
|
+
"type": "number",
|
|
330
|
+
"minimum": 0,
|
|
331
|
+
"maximum": 3600,
|
|
332
|
+
"default": 30,
|
|
333
|
+
"description": "How long `computer call` waits for another agent's lease before failing. `--wait` overrides per call."
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
"autoUpdate": {
|
|
338
|
+
"type": "object",
|
|
339
|
+
"additionalProperties": false,
|
|
340
|
+
"description": "Activity-fired project self-update (ADR 0229). Project config only; inert unless enabled is true. Prompts, session starts, and host CLI commands check a throttle stamp; when it is stale a detached child compares HEAD with the upstream branch (git ls-remote, no fetch), fingerprints the watched paths, and runs the steps only when something changed. Step protocol: each step runs from the project root with HARNERY_AUTOUPDATE_CHILD=1; a stdout or stderr line starting `harnery-notice:` becomes a notice shown once to every agent session in this checkout on its next prompt; exit code 75 means the step did what it could but needs a person, which is reported and not retried as a failure; any other non-zero exit stops the run and retries after a backoff.",
|
|
341
|
+
"properties": {
|
|
342
|
+
"enabled": {
|
|
343
|
+
"type": "boolean",
|
|
344
|
+
"default": false,
|
|
345
|
+
"description": "Turn self-update on for this project. Anything other than true leaves it off."
|
|
346
|
+
},
|
|
347
|
+
"steps": {
|
|
348
|
+
"type": "array",
|
|
349
|
+
"default": [],
|
|
350
|
+
"description": "Commands run in order when the upstream branch moved, a watched path changed, or the last run failed. Each entry is an argv array (no shell). The first failure stops the run. The host owns these commands: Harnery never fetches, resets, or discards changes itself.",
|
|
351
|
+
"items": {
|
|
352
|
+
"type": "array",
|
|
353
|
+
"minItems": 1,
|
|
354
|
+
"items": { "type": "string", "minLength": 1 }
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
"dailySteps": {
|
|
358
|
+
"type": "array",
|
|
359
|
+
"default": [],
|
|
360
|
+
"description": "Commands run at most once every 24 hours even when the checkout is current, for example a health check. Same argv form and protocol as steps.",
|
|
361
|
+
"items": {
|
|
362
|
+
"type": "array",
|
|
363
|
+
"minItems": 1,
|
|
364
|
+
"items": { "type": "string", "minLength": 1 }
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
"watch": {
|
|
368
|
+
"type": "array",
|
|
369
|
+
"default": [],
|
|
370
|
+
"description": "Paths outside Git whose change forces a run, such as managed tools or generated hook settings. `~/` and `$VAR` expand; relative paths resolve from the project root. A directory is fingerprinted shallowly (its own mtime plus each direct child's mtime and size).",
|
|
371
|
+
"items": { "type": "string", "minLength": 1 }
|
|
372
|
+
},
|
|
373
|
+
"checkMinutes": {
|
|
374
|
+
"type": "integer",
|
|
375
|
+
"minimum": 1,
|
|
376
|
+
"maximum": 1440,
|
|
377
|
+
"default": 15,
|
|
378
|
+
"description": "Minimum minutes between remote checks. Out-of-range values are clamped."
|
|
379
|
+
},
|
|
380
|
+
"timeoutMinutes": {
|
|
381
|
+
"type": "integer",
|
|
382
|
+
"minimum": 1,
|
|
383
|
+
"maximum": 240,
|
|
384
|
+
"default": 20,
|
|
385
|
+
"description": "Per-step timeout in minutes. A step that runs longer is stopped and counted as a failure. Out-of-range values are clamped."
|
|
386
|
+
},
|
|
387
|
+
"cleanup": {
|
|
388
|
+
"type": "boolean",
|
|
389
|
+
"default": true,
|
|
390
|
+
"description": "After a successful run, remove expired working artifacts (`artifacts clean --yes`) at most once a day."
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
},
|
|
311
394
|
"files": {
|
|
312
395
|
"type": "object",
|
|
313
396
|
"additionalProperties": false,
|
|
@@ -395,7 +478,7 @@
|
|
|
395
478
|
"type": "array",
|
|
396
479
|
"items": { "type": "string" },
|
|
397
480
|
"default": [],
|
|
398
|
-
"description": "Shipped Harnery skills this repo suppresses. Installed skill IDs are harn-decide, harn-council, and harn-
|
|
481
|
+
"description": "Shipped Harnery skills this repo suppresses. Installed skill IDs are harn-decide, harn-council, harn-end, harn-team, and (only when computer.enabled is true) harn-computer."
|
|
399
482
|
}
|
|
400
483
|
}
|
|
401
484
|
},
|
|
@@ -440,6 +523,11 @@
|
|
|
440
523
|
"description": "Windows command name the bridge installs, for example codex-wsl-hook. Only a bridge that implements the decision record 0197 contract should install it."
|
|
441
524
|
}
|
|
442
525
|
}
|
|
526
|
+
},
|
|
527
|
+
"portableCommands": {
|
|
528
|
+
"type": "boolean",
|
|
529
|
+
"default": false,
|
|
530
|
+
"description": "Write the same adapter hook commands on every OS: agent-hook from PATH as each hook's command, plus a matching Codex commandWindows, and have init --check treat that form as current everywhere. For a project that commits its adapter settings and is cloned on Windows, Linux, WSL and macOS; every clone needs agent-hook on PATH. Project config only."
|
|
443
531
|
}
|
|
444
532
|
}
|
|
445
533
|
},
|
package/src/commander.ts
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* (Commander pattern).
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { readFileSync } from "node:fs";
|
|
19
|
-
import { dirname, join } from "node:path";
|
|
18
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
19
|
+
import { dirname, join, resolve } from "node:path";
|
|
20
20
|
import { fileURLToPath } from "node:url";
|
|
21
21
|
import { Command } from "commander";
|
|
22
22
|
import {
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
loadAllLazyCommands,
|
|
26
26
|
registerLazyCommandBundles,
|
|
27
27
|
} from "./lazy-commands.ts";
|
|
28
|
+
import { coordEnv } from "./lib/env.ts";
|
|
28
29
|
|
|
29
30
|
export {
|
|
30
31
|
type LazyCommandBundle,
|
|
@@ -81,6 +82,9 @@ export interface EnvCheck {
|
|
|
81
82
|
*/
|
|
82
83
|
export type EnvSection = () => Promise<EnvCheck[]>;
|
|
83
84
|
|
|
85
|
+
/** Verdict from `HarneryProgramContext.headerPolicy` for one caller header. */
|
|
86
|
+
export type FetchHeaderDecision = { allow: true } | { allow: false; message: string };
|
|
87
|
+
|
|
84
88
|
export interface HarneryProgramContext {
|
|
85
89
|
/** Project name (e.g., "my-monorepo"). Used in user-facing log lines + telemetry tags. */
|
|
86
90
|
projectName?: string;
|
|
@@ -121,6 +125,17 @@ export interface HarneryProgramContext {
|
|
|
121
125
|
* zones behind a WAF). harn standalone skips the callback entirely.
|
|
122
126
|
*/
|
|
123
127
|
extraHeaders?: (url: string) => Record<string, string>;
|
|
128
|
+
/**
|
|
129
|
+
* Optional check run on every header the caller passed to `fetch` with
|
|
130
|
+
* `-H`, before any request leaves the process. Returning
|
|
131
|
+
* `{ allow: false, message }` stops the run with that message and a
|
|
132
|
+
* nonzero exit; there is no flag to override it. Only caller headers are
|
|
133
|
+
* checked: the default User-Agent and anything `extraHeaders` adds are the
|
|
134
|
+
* host's own. Redirect hops are not re-checked because a cross-origin hop
|
|
135
|
+
* already drops every caller header except a few browser headers. The
|
|
136
|
+
* message must not echo the header value. harn standalone checks nothing.
|
|
137
|
+
*/
|
|
138
|
+
headerPolicy?: (host: string, name: string, value: string) => FetchHeaderDecision;
|
|
124
139
|
/**
|
|
125
140
|
* Optional callback that returns extra cookies to persist into the shared
|
|
126
141
|
* jar before `fetch` / `browse` attach it. Runs only when a jar is in
|
|
@@ -130,12 +145,6 @@ export interface HarneryProgramContext {
|
|
|
130
145
|
* harn standalone skips the callback entirely.
|
|
131
146
|
*/
|
|
132
147
|
extraCookies?: import("./lib/cookies/index.ts").ExtraCookies;
|
|
133
|
-
/**
|
|
134
|
-
* When it returns true for a run, `fetch` follows redirects itself so the
|
|
135
|
-
* cookie header, `extraHeaders`, and Set-Cookie persistence apply to every
|
|
136
|
-
* hop instead of only the first request.
|
|
137
|
-
*/
|
|
138
|
-
clientRedirects?: () => boolean;
|
|
139
148
|
/**
|
|
140
149
|
* Called once per `browse` run. Returns the guard that inspects that
|
|
141
150
|
* session's live cookies before each request, after each response, and at
|
|
@@ -340,10 +349,75 @@ export function createHarneryProgram(opts: HarneryContextOpts = {}): Command {
|
|
|
340
349
|
bundle.commands.every((command) => !skip.has(commandName(command.command))),
|
|
341
350
|
);
|
|
342
351
|
registerLazyCommandBundles(program, bundles);
|
|
352
|
+
program.hook("preAction", async (_root, actionCommand) => {
|
|
353
|
+
await triggerAutoUpdate(actionCommand, opts.context);
|
|
354
|
+
});
|
|
343
355
|
|
|
344
356
|
return program;
|
|
345
357
|
}
|
|
346
358
|
|
|
359
|
+
let autoUpdateTriggered = false;
|
|
360
|
+
|
|
361
|
+
/** Top-level command name below the program for a Commander action command. */
|
|
362
|
+
function topLevelCommandName(command: Command): string {
|
|
363
|
+
let current = command;
|
|
364
|
+
while (current.parent?.parent) current = current.parent;
|
|
365
|
+
return current.name();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/** Nearest enclosing directory carrying `.harnery/`, without spawning Git. */
|
|
369
|
+
function cheapCoordRoot(context: HarneryProgramContext | undefined): string | null {
|
|
370
|
+
const override = coordEnv("COORD_ROOT_OVERRIDE");
|
|
371
|
+
if (override) return override;
|
|
372
|
+
if (context?.resolveCoordRoot) return context.resolveCoordRoot();
|
|
373
|
+
const starts = [process.env.CLAUDE_PROJECT_DIR, process.cwd()].filter(
|
|
374
|
+
(dir): dir is string => typeof dir === "string" && dir.length > 0,
|
|
375
|
+
);
|
|
376
|
+
for (const start of starts) {
|
|
377
|
+
let dir = resolve(start);
|
|
378
|
+
while (true) {
|
|
379
|
+
if (existsSync(join(dir, ".harnery"))) return dir;
|
|
380
|
+
const parent = dirname(dir);
|
|
381
|
+
if (parent === dir) break;
|
|
382
|
+
dir = parent;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Host CLI activity fires the project self-update check (ADR 0229) once per
|
|
390
|
+
* process. A project without an `autoUpdate` section pays a directory walk
|
|
391
|
+
* and one small file read; the engine module loads only when the section is
|
|
392
|
+
* present. Never throws and never delays the command beyond the tick's stat.
|
|
393
|
+
*/
|
|
394
|
+
async function triggerAutoUpdate(
|
|
395
|
+
actionCommand: Command,
|
|
396
|
+
context: HarneryProgramContext | undefined,
|
|
397
|
+
): Promise<void> {
|
|
398
|
+
if (autoUpdateTriggered) return;
|
|
399
|
+
autoUpdateTriggered = true;
|
|
400
|
+
try {
|
|
401
|
+
if (coordEnv("AUTOUPDATE_CHILD") === "1") return;
|
|
402
|
+
if (topLevelCommandName(actionCommand) === "autoupdate") return;
|
|
403
|
+
const root = cheapCoordRoot(context);
|
|
404
|
+
if (!root) return;
|
|
405
|
+
let text: string;
|
|
406
|
+
try {
|
|
407
|
+
text = readFileSync(join(root, ".harnery", "config.jsonc"), "utf8");
|
|
408
|
+
} catch {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (!text.includes('"autoUpdate"')) return;
|
|
412
|
+
const { readAutoUpdateConfig } = await import("./core/config.ts");
|
|
413
|
+
if (readAutoUpdateConfig(root)?.enabled !== true) return;
|
|
414
|
+
const { autoUpdateTick } = await import("./core/autoupdate/index.ts");
|
|
415
|
+
autoUpdateTick(root);
|
|
416
|
+
} catch {
|
|
417
|
+
// Self-update is best effort; the user's command always runs.
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
347
421
|
interface CommandBundleContext {
|
|
348
422
|
emit: EmitContext;
|
|
349
423
|
context: HarneryProgramContext | undefined;
|
|
@@ -545,6 +619,22 @@ function harneryCommandBundles({
|
|
|
545
619
|
async (program) =>
|
|
546
620
|
(await import("./commands/admission.ts")).registerAdmissionCommand(program, emit),
|
|
547
621
|
),
|
|
622
|
+
lazy(
|
|
623
|
+
"computer",
|
|
624
|
+
`Opt-in desktop control through a pinned desktop driver: \`computer status\`, \`install\`, \`up\`, \`call <tool> [json]\` under a desktop lease shared by every agent on the machine. Start with \`${binName} computer status\`.`,
|
|
625
|
+
async (program) =>
|
|
626
|
+
(await import("./commands/computer.ts")).registerComputerCommand(program, emit, binName),
|
|
627
|
+
),
|
|
628
|
+
lazy(
|
|
629
|
+
"autoupdate",
|
|
630
|
+
`Activity-fired project self-update: \`autoupdate status\`, \`check\`, \`run\`. Configure steps under \`autoUpdate\` in .harnery/config.jsonc; start with \`${binName} autoupdate status\`.`,
|
|
631
|
+
async (program) =>
|
|
632
|
+
(await import("./commands/autoupdate.ts")).registerAutoUpdateCommand(
|
|
633
|
+
program,
|
|
634
|
+
emit,
|
|
635
|
+
binName,
|
|
636
|
+
),
|
|
637
|
+
),
|
|
548
638
|
lazy(
|
|
549
639
|
"browse-session",
|
|
550
640
|
"Control an opted-in headed browse session through an owner-only descriptor",
|
package/src/commands/agents.ts
CHANGED
|
@@ -28,7 +28,7 @@ import { basename, join, resolve } from "node:path";
|
|
|
28
28
|
import type { Command } from "commander";
|
|
29
29
|
import type { EmitContext, HarneryProgramContext } from "../commander.ts";
|
|
30
30
|
import type { Adapter } from "../core/adapter.ts";
|
|
31
|
-
import { coordBinPath } from "../core/agents/coord-bin.ts";
|
|
31
|
+
import { coordBinPath, spawnCoordBinSync } from "../core/agents/coord-bin.ts";
|
|
32
32
|
import {
|
|
33
33
|
checkGitFinalization,
|
|
34
34
|
formatGitFinalizationFailure,
|
|
@@ -344,8 +344,14 @@ export function registerAgentsCommand(
|
|
|
344
344
|
|
|
345
345
|
cmd
|
|
346
346
|
.command("status")
|
|
347
|
-
.description(
|
|
347
|
+
.description(
|
|
348
|
+
"End-of-turn status box (name, task, lifecycle, files held, live peers, time; diagnostics only when they report a problem)",
|
|
349
|
+
)
|
|
348
350
|
.option("--json", "JSON output instead of the box")
|
|
351
|
+
.option(
|
|
352
|
+
"--full",
|
|
353
|
+
"Print every diagnostic row (session age, changes, activity, parent, context, resources)",
|
|
354
|
+
)
|
|
349
355
|
.option(
|
|
350
356
|
"--end-turn",
|
|
351
357
|
"Treat this as the turn's closing status: issue the box only when this session's held paths are committed and their repositories are pushed",
|
|
@@ -361,7 +367,13 @@ export function registerAgentsCommand(
|
|
|
361
367
|
"The Stop hook payload includes session_id; pass it through.",
|
|
362
368
|
)
|
|
363
369
|
.action(
|
|
364
|
-
(opts: {
|
|
370
|
+
(opts: {
|
|
371
|
+
endTurn?: boolean;
|
|
372
|
+
endSession?: boolean;
|
|
373
|
+
json?: boolean;
|
|
374
|
+
full?: boolean;
|
|
375
|
+
sessionId?: string;
|
|
376
|
+
}) => {
|
|
365
377
|
runStatus(opts);
|
|
366
378
|
},
|
|
367
379
|
);
|
|
@@ -1915,7 +1927,7 @@ function ensureAdapterSession(root: string): void {
|
|
|
1915
1927
|
: { source: "resume" }),
|
|
1916
1928
|
});
|
|
1917
1929
|
|
|
1918
|
-
|
|
1930
|
+
spawnCoordBinSync(agentHook, ["session-start", "--adapter", bootstrap.adapter], {
|
|
1919
1931
|
input: payload,
|
|
1920
1932
|
cwd: root,
|
|
1921
1933
|
encoding: "utf8",
|
|
@@ -1977,7 +1989,7 @@ function runReleaseClaim(path: string): void {
|
|
|
1977
1989
|
if (path.startsWith(`${root}/`)) canonical = path.slice(root.length + 1);
|
|
1978
1990
|
|
|
1979
1991
|
const helper = agentCoordOrExit(root);
|
|
1980
|
-
const result =
|
|
1992
|
+
const result = spawnCoordBinSync(helper, ["release-claim", myOwner, canonical], {
|
|
1981
1993
|
encoding: "utf8",
|
|
1982
1994
|
...coordHelperOpts(root),
|
|
1983
1995
|
});
|
|
@@ -2036,7 +2048,7 @@ function runSetTask(task: string, opts?: { sessionId?: string }): void {
|
|
|
2036
2048
|
|
|
2037
2049
|
// Heartbeat mutation goes through agent-coord (atomic temp+rename).
|
|
2038
2050
|
const helper = agentCoordOrExit(root);
|
|
2039
|
-
const result =
|
|
2051
|
+
const result = spawnCoordBinSync(helper, ["set-task", myOwner, task], {
|
|
2040
2052
|
encoding: "utf8",
|
|
2041
2053
|
...coordHelperOpts(root),
|
|
2042
2054
|
});
|
|
@@ -2364,6 +2376,7 @@ function runStatus(opts: {
|
|
|
2364
2376
|
endTurn?: boolean;
|
|
2365
2377
|
endSession?: boolean;
|
|
2366
2378
|
json?: boolean;
|
|
2379
|
+
full?: boolean;
|
|
2367
2380
|
sessionId?: string;
|
|
2368
2381
|
}): void {
|
|
2369
2382
|
const root = monorepoRoot();
|
|
@@ -2616,29 +2629,30 @@ function runStatus(opts: {
|
|
|
2616
2629
|
return;
|
|
2617
2630
|
}
|
|
2618
2631
|
|
|
2619
|
-
const
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2632
|
+
const contextPercent = !ctxUsage
|
|
2633
|
+
? null
|
|
2634
|
+
: ctxUsage.percentOnly
|
|
2635
|
+
? ctxUsage.usedPercent
|
|
2636
|
+
: (ctxUsage.used / ctxUsage.window) * 100;
|
|
2637
|
+
const rows = statusBoxRows({
|
|
2638
|
+
full: opts.full === true,
|
|
2639
|
+
session: formatAge(ageSecs),
|
|
2640
|
+
turn: turnElapsed?.value ?? null,
|
|
2641
|
+
changes: turnChanges.value,
|
|
2642
|
+
task: hb.task ?? "",
|
|
2643
|
+
activity: activityOf(hb),
|
|
2644
|
+
lifecycle: lifecycleLabel(hb),
|
|
2645
|
+
parent: describeParentReference(data.parent_reference),
|
|
2646
|
+
parentProblem: data.parent_reference.state === "conflict",
|
|
2647
|
+
context: ctxStr,
|
|
2648
|
+
contextProblem: contextPercent !== null && contextPercent >= STATUS_CONTEXT_WARN_PERCENT,
|
|
2649
|
+
resources: formatResourceSummary(resources),
|
|
2650
|
+
resourcesProblem:
|
|
2651
|
+
resources.assessment.state === "elevated" || resources.assessment.state === "critical",
|
|
2652
|
+
files: filesStr,
|
|
2653
|
+
peers: peersStr,
|
|
2654
|
+
time: timeStr,
|
|
2655
|
+
});
|
|
2642
2656
|
if (pendingCouncils.length > 0) {
|
|
2643
2657
|
// Slot the council line right before `time` so it stays in the "what's
|
|
2644
2658
|
// active for me" cluster of rows. Show the first ID + count; full list
|
|
@@ -2672,6 +2686,60 @@ function runStatus(opts: {
|
|
|
2672
2686
|
process.stdout.write(`${formatBox(displayName, rows)}\n`); // lint-ok-emission: chat-paste path; emit.text() auto-suppresses non-TTY
|
|
2673
2687
|
}
|
|
2674
2688
|
|
|
2689
|
+
/** Context use at which the compact status box adds its context row. */
|
|
2690
|
+
const STATUS_CONTEXT_WARN_PERCENT = 80;
|
|
2691
|
+
|
|
2692
|
+
/**
|
|
2693
|
+
* Rows of the status box. The compact default carries what an operator acts
|
|
2694
|
+
* on (task, lifecycle, files held, live peers, time, with the turn duration
|
|
2695
|
+
* beside the time); diagnostic rows appear only when they report a problem.
|
|
2696
|
+
* `--full` restores every row. The Stop hook detects the box by its
|
|
2697
|
+
* `┌─ agent-` title, never by a row, so trimming rows cannot break it.
|
|
2698
|
+
*/
|
|
2699
|
+
export function statusBoxRows(input: {
|
|
2700
|
+
full: boolean;
|
|
2701
|
+
session: string;
|
|
2702
|
+
turn: string | null;
|
|
2703
|
+
changes: string;
|
|
2704
|
+
task: string;
|
|
2705
|
+
activity: string;
|
|
2706
|
+
lifecycle: string;
|
|
2707
|
+
parent: string;
|
|
2708
|
+
parentProblem: boolean;
|
|
2709
|
+
context: string;
|
|
2710
|
+
contextProblem: boolean;
|
|
2711
|
+
resources: string;
|
|
2712
|
+
resourcesProblem: boolean;
|
|
2713
|
+
files: string;
|
|
2714
|
+
peers: string;
|
|
2715
|
+
time: string;
|
|
2716
|
+
}): Array<[string, string]> {
|
|
2717
|
+
const task: Array<[string, string]> = input.task ? [["task", input.task]] : [];
|
|
2718
|
+
if (input.full) {
|
|
2719
|
+
return [
|
|
2720
|
+
["session", input.session],
|
|
2721
|
+
...(input.turn ? ([["turn", input.turn]] as Array<[string, string]>) : []),
|
|
2722
|
+
["changes", input.changes],
|
|
2723
|
+
...task,
|
|
2724
|
+
["activity", input.activity],
|
|
2725
|
+
["lifecycle", input.lifecycle],
|
|
2726
|
+
["parent", input.parent],
|
|
2727
|
+
["context", input.context],
|
|
2728
|
+
["resources", input.resources],
|
|
2729
|
+
["files", input.files],
|
|
2730
|
+
["peers", input.peers],
|
|
2731
|
+
["time", input.time],
|
|
2732
|
+
];
|
|
2733
|
+
}
|
|
2734
|
+
const rows: Array<[string, string]> = [...task, ["lifecycle", input.lifecycle]];
|
|
2735
|
+
if (input.parentProblem) rows.push(["parent", input.parent]);
|
|
2736
|
+
if (input.contextProblem) rows.push(["context", input.context]);
|
|
2737
|
+
if (input.resourcesProblem) rows.push(["resources", input.resources]);
|
|
2738
|
+
rows.push(["files", input.files], ["peers", input.peers]);
|
|
2739
|
+
rows.push(["time", input.turn ? `${input.time} (turn ${input.turn})` : input.time]);
|
|
2740
|
+
return rows;
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2675
2743
|
function qualityForStatus(
|
|
2676
2744
|
mode: "off" | "shadow" | "report",
|
|
2677
2745
|
snapshot: RunQualitySnapshot | null,
|
|
@@ -4792,7 +4860,7 @@ function replayAdapterSamples(
|
|
|
4792
4860
|
}
|
|
4793
4861
|
|
|
4794
4862
|
const subcommand = EVENT_SUBCOMMAND[event] ?? event;
|
|
4795
|
-
const dispatch =
|
|
4863
|
+
const dispatch = spawnCoordBinSync(agentHook, [subcommand, "--adapter", adapterFlag], {
|
|
4796
4864
|
cwd: sandbox,
|
|
4797
4865
|
encoding: "utf8",
|
|
4798
4866
|
input: JSON.stringify(payloadObj),
|
|
@@ -5371,7 +5439,7 @@ function runHeal(opts: {
|
|
|
5371
5439
|
|
|
5372
5440
|
// Both recovery actions are handled by the bundled agent-coord binary.
|
|
5373
5441
|
const helper = agentCoordOrExit(root);
|
|
5374
|
-
let proc =
|
|
5442
|
+
let proc = spawnCoordBinSync(helper, helperArgs, {
|
|
5375
5443
|
encoding: "utf8",
|
|
5376
5444
|
...coordHelperOpts(root),
|
|
5377
5445
|
});
|
|
@@ -5412,7 +5480,7 @@ function runHeal(opts: {
|
|
|
5412
5480
|
"the current native session could not establish authority; no fallback cache was accepted",
|
|
5413
5481
|
);
|
|
5414
5482
|
}
|
|
5415
|
-
proc =
|
|
5483
|
+
proc = spawnCoordBinSync(helper, helperArgs, {
|
|
5416
5484
|
encoding: "utf8",
|
|
5417
5485
|
...coordHelperOpts(root),
|
|
5418
5486
|
});
|