harnery 0.36.0 → 0.37.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/dist/commander.js +4 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +87 -46
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +26 -1
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +11 -0
- package/dist/commands/diagnostics.d.ts +4 -0
- package/dist/commands/diagnostics.d.ts.map +1 -0
- package/dist/commands/diagnostics.js +229 -0
- package/dist/commands/files.d.ts +12 -0
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +54 -1
- package/dist/commands/governor.d.ts.map +1 -1
- package/dist/commands/governor.js +4 -0
- package/dist/commands/ledger-v3.d.ts.map +1 -1
- package/dist/commands/ledger-v3.js +71 -2
- package/dist/commands/qa-run.d.ts +4 -0
- package/dist/commands/qa-run.d.ts.map +1 -0
- package/dist/commands/qa-run.js +141 -0
- package/dist/commands/resources.d.ts +4 -0
- package/dist/commands/resources.d.ts.map +1 -0
- package/dist/commands/resources.js +29 -0
- package/dist/commands/supervisor.d.ts +4 -0
- package/dist/commands/supervisor.d.ts.map +1 -0
- package/dist/commands/supervisor.js +101 -0
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +17 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +4 -0
- package/dist/core/agents/cli.js +7 -3
- package/dist/core/agents/health.d.ts +17 -0
- package/dist/core/agents/health.d.ts.map +1 -0
- package/dist/core/agents/health.js +109 -0
- package/dist/core/agents/mailbox.d.ts +92 -0
- package/dist/core/agents/mailbox.d.ts.map +1 -0
- package/dist/core/agents/mailbox.js +325 -0
- package/dist/core/agents/reconcile-coordination-v3.d.ts +44 -0
- package/dist/core/agents/reconcile-coordination-v3.d.ts.map +1 -0
- package/dist/core/agents/reconcile-coordination-v3.js +58 -0
- package/dist/core/agents/render/prompt-context.d.ts +5 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +34 -13
- package/dist/core/agents/render/session-context.d.ts +8 -0
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +34 -3
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +50 -1
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +6 -10
- package/dist/core/agents/session-finalizer-v3.js +4 -1
- package/dist/core/artifacts/index.d.ts +26 -1
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +146 -6
- package/dist/core/config.d.ts +12 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +43 -0
- package/dist/core/diagnostics/advice.d.ts +9 -0
- package/dist/core/diagnostics/advice.d.ts.map +1 -0
- package/dist/core/diagnostics/advice.js +112 -0
- package/dist/core/diagnostics/bundle.d.ts +35 -0
- package/dist/core/diagnostics/bundle.d.ts.map +1 -0
- package/dist/core/diagnostics/bundle.js +874 -0
- package/dist/core/diagnostics/comparison.d.ts +5 -0
- package/dist/core/diagnostics/comparison.d.ts.map +1 -0
- package/dist/core/diagnostics/comparison.js +444 -0
- package/dist/core/diagnostics/contract.d.ts +245 -0
- package/dist/core/diagnostics/contract.d.ts.map +1 -0
- package/dist/core/diagnostics/contract.js +18 -0
- package/dist/core/diagnostics/identity.d.ts +3 -0
- package/dist/core/diagnostics/identity.d.ts.map +1 -0
- package/dist/core/diagnostics/identity.js +10 -0
- package/dist/core/diagnostics/index.d.ts +8 -0
- package/dist/core/diagnostics/index.d.ts.map +1 -0
- package/dist/core/diagnostics/index.js +7 -0
- package/dist/core/diagnostics/replay.d.ts +8 -0
- package/dist/core/diagnostics/replay.d.ts.map +1 -0
- package/dist/core/diagnostics/replay.js +213 -0
- package/dist/core/diagnostics/sanitize.d.ts +9 -0
- package/dist/core/diagnostics/sanitize.d.ts.map +1 -0
- package/dist/core/diagnostics/sanitize.js +84 -0
- package/dist/core/events/legacy-storage/compression.d.ts +13 -0
- package/dist/core/events/legacy-storage/compression.d.ts.map +1 -0
- package/dist/core/events/legacy-storage/compression.js +107 -0
- package/dist/core/events/legacy-storage/index.d.ts +1 -0
- package/dist/core/events/legacy-storage/index.d.ts.map +1 -1
- package/dist/core/events/legacy-storage/index.js +1 -0
- package/dist/core/events/v3/archive-retention.d.ts +33 -0
- package/dist/core/events/v3/archive-retention.d.ts.map +1 -0
- package/dist/core/events/v3/archive-retention.js +195 -0
- package/dist/core/events/v3/capabilities.d.ts +1 -1
- package/dist/core/events/v3/capabilities.d.ts.map +1 -1
- package/dist/core/events/v3/capabilities.js +1 -0
- package/dist/core/events/v3/index.d.ts +1 -0
- package/dist/core/events/v3/index.d.ts.map +1 -1
- package/dist/core/events/v3/index.js +1 -0
- package/dist/core/events/v3/live-routing.d.ts.map +1 -1
- package/dist/core/events/v3/live-routing.js +1 -0
- package/dist/core/events/v3/producers/hook-base.d.ts +1 -1
- package/dist/core/events/v3/producers/hook-base.d.ts.map +1 -1
- package/dist/core/events/v3/producers/hook-base.js +13 -2
- package/dist/core/events/v3/producers/intake.d.ts.map +1 -1
- package/dist/core/events/v3/producers/intake.js +1 -0
- package/dist/core/events/v3/producers/recorder.d.ts +16 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +88 -1
- package/dist/core/hooks/adapter/output.d.ts +8 -0
- package/dist/core/hooks/adapter/output.d.ts.map +1 -1
- package/dist/core/hooks/adapter/output.js +11 -1
- package/dist/core/hooks/cli.js +183 -192
- package/dist/core/hooks/health.d.ts +59 -0
- package/dist/core/hooks/health.d.ts.map +1 -0
- package/dist/core/hooks/health.js +84 -0
- package/dist/core/resources/contract.d.ts +90 -0
- package/dist/core/resources/contract.d.ts.map +1 -0
- package/dist/core/resources/contract.js +2 -0
- package/dist/core/resources/index.d.ts +6 -0
- package/dist/core/resources/index.d.ts.map +1 -0
- package/dist/core/resources/index.js +5 -0
- package/dist/core/resources/sampler.d.ts +27 -0
- package/dist/core/resources/sampler.d.ts.map +1 -0
- package/dist/core/resources/sampler.js +357 -0
- package/dist/core/resources/service-status.d.ts +3 -0
- package/dist/core/resources/service-status.d.ts.map +1 -0
- package/dist/core/resources/service-status.js +48 -0
- package/dist/core/resources/service.d.ts +26 -0
- package/dist/core/resources/service.d.ts.map +1 -0
- package/dist/core/resources/service.js +292 -0
- package/dist/core/resources/storage.d.ts +13 -0
- package/dist/core/resources/storage.d.ts.map +1 -0
- package/dist/core/resources/storage.js +30 -0
- package/dist/core/storage/atomic-json.d.ts +3 -0
- package/dist/core/storage/atomic-json.d.ts.map +1 -0
- package/dist/core/storage/atomic-json.js +15 -0
- package/dist/core/storage/builtins.d.ts.map +1 -1
- package/dist/core/storage/builtins.js +45 -6
- package/dist/core/storage/logger.d.ts +2 -0
- package/dist/core/storage/logger.d.ts.map +1 -1
- package/dist/core/storage/logger.js +2 -0
- package/dist/core/storage/query.d.ts +14 -0
- package/dist/core/storage/query.d.ts.map +1 -1
- package/dist/core/storage/query.js +51 -0
- package/dist/core/supervisor/activity.d.ts +4 -0
- package/dist/core/supervisor/activity.d.ts.map +1 -0
- package/dist/core/supervisor/activity.js +141 -0
- package/dist/core/supervisor/contract.d.ts +268 -0
- package/dist/core/supervisor/contract.d.ts.map +1 -0
- package/dist/core/supervisor/contract.js +35 -0
- package/dist/core/supervisor/explanations.d.ts +4 -0
- package/dist/core/supervisor/explanations.d.ts.map +1 -0
- package/dist/core/supervisor/explanations.js +71 -0
- package/dist/core/supervisor/findings.d.ts +20 -0
- package/dist/core/supervisor/findings.d.ts.map +1 -0
- package/dist/core/supervisor/findings.js +399 -0
- package/dist/core/supervisor/history.d.ts +9 -0
- package/dist/core/supervisor/history.d.ts.map +1 -0
- package/dist/core/supervisor/history.js +46 -0
- package/dist/core/supervisor/hook-health-alerts.d.ts +14 -0
- package/dist/core/supervisor/hook-health-alerts.d.ts.map +1 -0
- package/dist/core/supervisor/hook-health-alerts.js +70 -0
- package/dist/core/supervisor/hook-health-storage.d.ts +5 -0
- package/dist/core/supervisor/hook-health-storage.d.ts.map +1 -0
- package/dist/core/supervisor/hook-health-storage.js +24 -0
- package/dist/core/supervisor/hook-health.d.ts +72 -0
- package/dist/core/supervisor/hook-health.d.ts.map +1 -0
- package/dist/core/supervisor/hook-health.js +203 -0
- package/dist/core/supervisor/hooks.d.ts +10 -0
- package/dist/core/supervisor/hooks.d.ts.map +1 -0
- package/dist/core/supervisor/hooks.js +28 -0
- package/dist/core/supervisor/index.d.ts +15 -0
- package/dist/core/supervisor/index.d.ts.map +1 -0
- package/dist/core/supervisor/index.js +14 -0
- package/dist/core/supervisor/log-feed.d.ts +9 -0
- package/dist/core/supervisor/log-feed.d.ts.map +1 -0
- package/dist/core/supervisor/log-feed.js +103 -0
- package/dist/core/supervisor/service.d.ts +29 -0
- package/dist/core/supervisor/service.d.ts.map +1 -0
- package/dist/core/supervisor/service.js +450 -0
- package/dist/core/supervisor/services.d.ts +11 -0
- package/dist/core/supervisor/services.d.ts.map +1 -0
- package/dist/core/supervisor/services.js +155 -0
- package/dist/core/supervisor/status.d.ts +3 -0
- package/dist/core/supervisor/status.d.ts.map +1 -0
- package/dist/core/supervisor/status.js +48 -0
- package/dist/core/supervisor/storage.d.ts +30 -0
- package/dist/core/supervisor/storage.d.ts.map +1 -0
- package/dist/core/supervisor/storage.js +73 -0
- package/dist/core/supervisor/timeline.d.ts +3 -0
- package/dist/core/supervisor/timeline.d.ts.map +1 -0
- package/dist/core/supervisor/timeline.js +87 -0
- package/dist/core/workflow/admission.d.ts +23 -0
- package/dist/core/workflow/admission.d.ts.map +1 -0
- package/dist/core/workflow/admission.js +136 -0
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +77 -2
- package/dist/core/workflow/index.d.ts +3 -2
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -1
- package/dist/core/workflow/proof.d.ts +2 -1
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +39 -1
- package/dist/core/workflow/run-state.d.ts +3 -2
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +8 -1
- package/dist/core/workflow/types.d.ts +33 -1
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/types.js +2 -1
- package/dist/lib/browser/client.d.ts +5 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +1 -0
- package/dist/lib/browser/index.d.ts +2 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +2 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +173 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -0
- package/dist/lib/browser/qa-run-contracts.js +226 -0
- package/dist/lib/browser/qa-run.d.ts +51 -0
- package/dist/lib/browser/qa-run.d.ts.map +1 -0
- package/dist/lib/browser/qa-run.js +684 -0
- package/dist/lib/coord-root-id.d.ts +9 -0
- package/dist/lib/coord-root-id.d.ts.map +1 -0
- package/dist/lib/coord-root-id.js +17 -0
- package/dist/lib/instructions/apply.d.ts +3 -1
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +74 -3
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +6 -0
- package/package.json +12 -1
- package/src/commander.ts +26 -0
- package/src/commands/agents.ts +102 -49
- package/src/commands/artifacts.ts +40 -2
- package/src/commands/browse.ts +15 -0
- package/src/commands/diagnostics.ts +271 -0
- package/src/commands/files.ts +102 -2
- package/src/commands/governor.ts +8 -0
- package/src/commands/ledger-v3.ts +82 -0
- package/src/commands/qa-run.ts +184 -0
- package/src/commands/resources.ts +38 -0
- package/src/commands/supervisor.ts +141 -0
- package/src/commands/web.ts +19 -0
- package/src/commands/workflow.ts +8 -0
- package/src/core/agents/cli.ts +7 -3
- package/src/core/agents/health.ts +142 -0
- package/src/core/agents/mailbox.ts +373 -0
- package/src/core/agents/reconcile-coordination-v3.ts +77 -0
- package/src/core/agents/render/prompt-context.ts +42 -12
- package/src/core/agents/render/session-context.ts +37 -3
- package/src/core/agents/rules/commit-conflict.ts +58 -1
- package/src/core/agents/rules/stop-hook.ts +6 -12
- package/src/core/agents/session-finalizer-v3.ts +4 -1
- package/src/core/artifacts/index.ts +196 -10
- package/src/core/config.ts +111 -2
- package/src/core/diagnostics/advice.ts +147 -0
- package/src/core/diagnostics/bundle.ts +1142 -0
- package/src/core/diagnostics/comparison.ts +565 -0
- package/src/core/diagnostics/contract.ts +311 -0
- package/src/core/diagnostics/identity.ts +11 -0
- package/src/core/diagnostics/index.ts +7 -0
- package/src/core/diagnostics/replay.ts +314 -0
- package/src/core/diagnostics/sanitize.ts +97 -0
- package/src/core/events/legacy-storage/compression.ts +130 -0
- package/src/core/events/legacy-storage/index.ts +1 -0
- package/src/core/events/v3/archive-retention.ts +251 -0
- package/src/core/events/v3/capabilities.ts +2 -0
- package/src/core/events/v3/index.ts +9 -0
- package/src/core/events/v3/live-routing.ts +1 -0
- package/src/core/events/v3/producers/hook-base.ts +14 -2
- package/src/core/events/v3/producers/intake.ts +1 -0
- package/src/core/events/v3/producers/recorder.ts +110 -1
- package/src/core/hooks/adapter/output.ts +12 -1
- package/src/core/hooks/cli.ts +192 -215
- package/src/core/hooks/health.ts +146 -0
- package/src/core/resources/contract.ts +98 -0
- package/src/core/resources/index.ts +5 -0
- package/src/core/resources/sampler.ts +458 -0
- package/src/core/resources/service-status.ts +62 -0
- package/src/core/resources/service.ts +337 -0
- package/src/core/resources/storage.ts +44 -0
- package/src/core/storage/atomic-json.ts +15 -0
- package/src/core/storage/builtins.ts +62 -6
- package/src/core/storage/logger.ts +2 -0
- package/src/core/storage/query.ts +58 -0
- package/src/core/supervisor/activity.ts +193 -0
- package/src/core/supervisor/contract.ts +312 -0
- package/src/core/supervisor/explanations.ts +104 -0
- package/src/core/supervisor/findings.ts +788 -0
- package/src/core/supervisor/history.ts +61 -0
- package/src/core/supervisor/hook-health-alerts.ts +108 -0
- package/src/core/supervisor/hook-health-storage.ts +25 -0
- package/src/core/supervisor/hook-health.ts +301 -0
- package/src/core/supervisor/hooks.ts +35 -0
- package/src/core/supervisor/index.ts +14 -0
- package/src/core/supervisor/log-feed.ts +131 -0
- package/src/core/supervisor/service.ts +511 -0
- package/src/core/supervisor/services.ts +203 -0
- package/src/core/supervisor/status.ts +59 -0
- package/src/core/supervisor/storage.ts +126 -0
- package/src/core/supervisor/timeline.ts +133 -0
- package/src/core/workflow/admission.ts +215 -0
- package/src/core/workflow/engine.ts +93 -1
- package/src/core/workflow/index.ts +10 -0
- package/src/core/workflow/proof.ts +54 -1
- package/src/core/workflow/run-state.ts +13 -1
- package/src/core/workflow/types.ts +39 -1
- package/src/lib/browser/client.ts +6 -0
- package/src/lib/browser/index.ts +29 -0
- package/src/lib/browser/qa-run-contracts.ts +380 -0
- package/src/lib/browser/qa-run.ts +784 -0
- package/src/lib/coord-root-id.ts +23 -0
- package/src/lib/instructions/apply.ts +81 -2
- package/src/lib/instructions/templates.ts +6 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coord-root-id.d.ts","sourceRoot":"","sources":["../../src/lib/coord-root-id.ts"],"names":[],"mappings":"AAIA,mFAAmF;AACnF,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED,wBAAgB,mBAAmB,CACjC,IAAI,GAAE,MAAM,GAAG,MAAyB,EACxC,QAAQ,SAAU,GACjB,MAAM,CAGR"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { DEFAULT_WEB_PORT } from "./local-file-url.js";
|
|
4
|
+
/** Local-only endpoint used to prove that a dashboard serves the caller's repo. */
|
|
5
|
+
export const COORD_ROOT_ID_PATH = "/api/coord-root";
|
|
6
|
+
/**
|
|
7
|
+
* Opaque identity for a canonical coordination root. The path stays local;
|
|
8
|
+
* only its digest crosses the dashboard boundary.
|
|
9
|
+
*/
|
|
10
|
+
export function coordRootId(root) {
|
|
11
|
+
const canonical = realpathSync(root);
|
|
12
|
+
return `sha256:${createHash("sha256").update(canonical).digest("hex")}`;
|
|
13
|
+
}
|
|
14
|
+
export function localCoordRootIdUrl(port = DEFAULT_WEB_PORT, protocol = "http:") {
|
|
15
|
+
const portPart = String(port) === "" ? "" : `:${port}`;
|
|
16
|
+
return `${protocol}//127.0.0.1${portPart}${COORD_ROOT_ID_PATH}`;
|
|
17
|
+
}
|
|
@@ -7,13 +7,15 @@
|
|
|
7
7
|
* writes, and deletes so init/deinit stay thin and this stays integration-
|
|
8
8
|
* testable against a temp dir.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Three files, one dir:
|
|
11
11
|
* - `AGENTS.md` — the always-on orientation block (a managed region; the
|
|
12
12
|
* consumer owns the rest of the file).
|
|
13
13
|
* - `CLAUDE.md` — claude-code only; Claude Code reads CLAUDE.md, not AGENTS.md,
|
|
14
14
|
* so a fresh consumer gets a CLAUDE.md whose managed region imports
|
|
15
15
|
* `@AGENTS.md`. A CLAUDE.md that already imports AGENTS.md or already carries
|
|
16
16
|
* the block (a host that generates CLAUDE.md from AGENTS.md) is left alone.
|
|
17
|
+
* - `.cursor/rules/harnery-turn-ritual.mdc` — cursor only; an Always Apply
|
|
18
|
+
* rule that makes the Stop-enforced status footer visible in reply text.
|
|
17
19
|
* - `<adapter-skill-dir>/<skill>/SKILL.md` — fully-owned files, honoring
|
|
18
20
|
* `skills.exclude` in `.harnery/config.jsonc`. Claude Code uses
|
|
19
21
|
* `.claude/skills/`; Cursor and Codex use `.agents/skills/`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/lib/instructions/apply.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/lib/instructions/apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAyFH,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAYlE;AAmBD,UAAU,SAAS;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,WAAW,CAoInF;AAED,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,WAAW,CA8FrF;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IACpC,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACzC,WAAW,CA2Db"}
|
|
@@ -7,25 +7,28 @@
|
|
|
7
7
|
* writes, and deletes so init/deinit stay thin and this stays integration-
|
|
8
8
|
* testable against a temp dir.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Three files, one dir:
|
|
11
11
|
* - `AGENTS.md` — the always-on orientation block (a managed region; the
|
|
12
12
|
* consumer owns the rest of the file).
|
|
13
13
|
* - `CLAUDE.md` — claude-code only; Claude Code reads CLAUDE.md, not AGENTS.md,
|
|
14
14
|
* so a fresh consumer gets a CLAUDE.md whose managed region imports
|
|
15
15
|
* `@AGENTS.md`. A CLAUDE.md that already imports AGENTS.md or already carries
|
|
16
16
|
* the block (a host that generates CLAUDE.md from AGENTS.md) is left alone.
|
|
17
|
+
* - `.cursor/rules/harnery-turn-ritual.mdc` — cursor only; an Always Apply
|
|
18
|
+
* rule that makes the Stop-enforced status footer visible in reply text.
|
|
17
19
|
* - `<adapter-skill-dir>/<skill>/SKILL.md` — fully-owned files, honoring
|
|
18
20
|
* `skills.exclude` in `.harnery/config.jsonc`. Claude Code uses
|
|
19
21
|
* `.claude/skills/`; Cursor and Codex use `.agents/skills/`.
|
|
20
22
|
*/
|
|
21
23
|
import { existsSync, mkdirSync, readdirSync, readFileSync, rmdirSync, rmSync, writeFileSync, } from "node:fs";
|
|
22
24
|
import { dirname, join } from "node:path";
|
|
23
|
-
import { stripJsonComments } from "../../core/config.js";
|
|
25
|
+
import { endOfTurnStatusCommand, stripJsonComments } from "../../core/config.js";
|
|
24
26
|
import { readHostAddendum } from "./host-addendum.js";
|
|
25
|
-
import { checkOwnedSkill, checkRegion, isOwnedFile, removeRegion, spliceRegion, } from "./splice.js";
|
|
27
|
+
import { checkOwnedSkill, checkRegion, isOwnedFile, removeRegion, shortHash, spliceRegion, } from "./splice.js";
|
|
26
28
|
import { HOST_ADDENDUM_REGION, IMPORT_REGION, INSTRUCTIONS_REGION, renderInstructionsBlock, SKILLS, } from "./templates.js";
|
|
27
29
|
const AGENTS_FILE = "AGENTS.md";
|
|
28
30
|
const CLAUDE_FILE = "CLAUDE.md";
|
|
31
|
+
const CURSOR_RULE_FILE = join(".cursor", "rules", "harnery-turn-ritual.mdc");
|
|
29
32
|
const ADAPTER_SKILLS_DIR = {
|
|
30
33
|
"claude-code": join(".claude", "skills"),
|
|
31
34
|
cursor: join(".agents", "skills"),
|
|
@@ -43,6 +46,36 @@ function skillBody(render, binName) {
|
|
|
43
46
|
const content = render(binName);
|
|
44
47
|
return content.slice(content.indexOf("-->") + 3).trim();
|
|
45
48
|
}
|
|
49
|
+
function cursorRuleBody(projectRoot, binName) {
|
|
50
|
+
const statusCommand = endOfTurnStatusCommand(projectRoot);
|
|
51
|
+
return [
|
|
52
|
+
"# Harnery turn ritual",
|
|
53
|
+
"",
|
|
54
|
+
"On every turn that uses tools:",
|
|
55
|
+
"",
|
|
56
|
+
`1. Run \`${binName} agents set-task "<short focus>"\` during the turn, even when the focus is unchanged.`,
|
|
57
|
+
`2. Make \`${statusCommand}\` your final tool call.`,
|
|
58
|
+
"3. Append that command's stdout verbatim in a fenced code block at the bottom of the same substantive reply.",
|
|
59
|
+
"",
|
|
60
|
+
"Cursor Stop enforces all three signals. A missing signal produces one follow-up that names every repair needed.",
|
|
61
|
+
].join("\n");
|
|
62
|
+
}
|
|
63
|
+
function cursorRuleContent(projectRoot, binName) {
|
|
64
|
+
const body = cursorRuleBody(projectRoot, binName);
|
|
65
|
+
const marker = `<!-- harnery:generated cursor-turn-ritual v=${shortHash(body)}; machine-owned; ` +
|
|
66
|
+
`regenerated by \`${binName} init\`, removed by \`${binName} deinit\`. ` +
|
|
67
|
+
"Edit the harnery template, not this file. -->";
|
|
68
|
+
return [
|
|
69
|
+
"---",
|
|
70
|
+
"description: Harnery end-of-turn coordination ritual",
|
|
71
|
+
"alwaysApply: true",
|
|
72
|
+
"---",
|
|
73
|
+
marker,
|
|
74
|
+
"",
|
|
75
|
+
body,
|
|
76
|
+
"",
|
|
77
|
+
].join("\n");
|
|
78
|
+
}
|
|
46
79
|
/** Read `skills.exclude` from `.harnery/config.jsonc` (absent/unparseable → none). */
|
|
47
80
|
export function readSkillsExclude(projectRoot) {
|
|
48
81
|
const p = join(projectRoot, ".harnery", "config.jsonc");
|
|
@@ -157,6 +190,26 @@ export function applyInstructions(projectRoot, opts) {
|
|
|
157
190
|
}
|
|
158
191
|
}
|
|
159
192
|
}
|
|
193
|
+
// ── Cursor Always Apply rule ────────────────────────────────────────────
|
|
194
|
+
if (opts.adapter === "cursor") {
|
|
195
|
+
const rulePath = join(projectRoot, CURSOR_RULE_FILE);
|
|
196
|
+
const content = cursorRuleContent(projectRoot, opts.binName);
|
|
197
|
+
const before = existsSync(rulePath) ? readFileSync(rulePath, "utf8") : null;
|
|
198
|
+
if (before === content) {
|
|
199
|
+
actions.push(`· ${CURSOR_RULE_FILE} already current`);
|
|
200
|
+
}
|
|
201
|
+
else if (before !== null && !isOwnedFile(before)) {
|
|
202
|
+
warnings.push(`left ${CURSOR_RULE_FILE} (hand-edited; no harnery ownership marker)`);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
if (!opts.dryRun) {
|
|
206
|
+
mkdirSync(dirname(rulePath), { recursive: true });
|
|
207
|
+
writeFileSync(rulePath, content);
|
|
208
|
+
}
|
|
209
|
+
const fresh = before === null;
|
|
210
|
+
actions.push(`${fresh ? "+" : "~"} ${verbed(fresh ? "write" : "update", fresh ? "wrote" : "updated")} ${CURSOR_RULE_FILE}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
160
213
|
// ── shipped skills ───────────────────────────────────────────────────────
|
|
161
214
|
if (skillsDir) {
|
|
162
215
|
const exclude = readSkillsExclude(projectRoot);
|
|
@@ -239,6 +292,19 @@ export function removeInstructions(projectRoot, opts) {
|
|
|
239
292
|
}
|
|
240
293
|
}
|
|
241
294
|
}
|
|
295
|
+
if (opts.adapter === "cursor") {
|
|
296
|
+
const rulePath = join(projectRoot, CURSOR_RULE_FILE);
|
|
297
|
+
if (existsSync(rulePath)) {
|
|
298
|
+
if (!isOwnedFile(readFileSync(rulePath, "utf8"))) {
|
|
299
|
+
warnings.push(`left ${CURSOR_RULE_FILE} (hand-edited; no harnery ownership marker)`);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
if (!opts.dryRun)
|
|
303
|
+
rmSync(rulePath);
|
|
304
|
+
actions.push(`+ ${opts.dryRun ? "would delete" : "deleted"} ${CURSOR_RULE_FILE}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
242
308
|
// ── shipped skills ───────────────────────────────────────────────────────
|
|
243
309
|
if (skillsDir) {
|
|
244
310
|
for (const skill of SKILLS) {
|
|
@@ -305,6 +371,11 @@ export function checkInstructions(projectRoot, opts) {
|
|
|
305
371
|
note(`skill ${skill.id}`, checkOwnedSkill(c, skillBody(skill.render, opts.binName)));
|
|
306
372
|
}
|
|
307
373
|
}
|
|
374
|
+
if (opts.adapter === "cursor") {
|
|
375
|
+
const rulePath = join(projectRoot, CURSOR_RULE_FILE);
|
|
376
|
+
const content = existsSync(rulePath) ? readFileSync(rulePath, "utf8") : "";
|
|
377
|
+
note(`${CURSOR_RULE_FILE} rule`, checkOwnedSkill(content, cursorRuleBody(projectRoot, opts.binName)));
|
|
378
|
+
}
|
|
308
379
|
}
|
|
309
380
|
catch (err) {
|
|
310
381
|
errored = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/lib/instructions/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAClD,sEAAsE;AACtE,eAAO,MAAM,aAAa,WAAW,CAAC;AACtC;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,MAAM,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,2CAA2C;IAC3C,GAAG,EAAE,OAAO,CAAC;IACb,4CAA4C;IAC5C,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,WAAoE,GAC3E,MAAM,
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/lib/instructions/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAClD,sEAAsE;AACtE,eAAO,MAAM,aAAa,WAAW,CAAC;AACtC;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,8CAA8C;IAC9C,MAAM,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,2CAA2C;IAC3C,GAAG,EAAE,OAAO,CAAC;IACb,4CAA4C;IAC5C,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,WAAoE,GAC3E,MAAM,CAgHR;AAID,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAmVD,eAAO,MAAM,MAAM,EAAE,aAAa,EAiDjC,CAAC"}
|
|
@@ -113,6 +113,12 @@ coordination ledger (\`.harnery/ledgers/v3/\`). Lead a shell command with a
|
|
|
113
113
|
carries a reason instead of \`(no intent)\`; the [tool-intent
|
|
114
114
|
guide](https://harnery.com/guides/tool-intent/) owns the details.
|
|
115
115
|
|
|
116
|
+
**Messaging another agent.** \`${b} agents ping <name> "<message>"\` reaches an
|
|
117
|
+
agent by name whether or not they are running: a live agent sees it on their next
|
|
118
|
+
prompt, and a dormant name holds it until a session of that name next starts.
|
|
119
|
+
Send it as your first and only step. Do not check who is live first, and do not
|
|
120
|
+
hunt for somewhere else to leave it. Only a never-used name is refused.
|
|
121
|
+
|
|
116
122
|
**Journal.** \`${b} journal add <category> "<text>"\` (category = ${journalCats})
|
|
117
123
|
leaves breadcrumbs that survive context compaction;
|
|
118
124
|
\`${b} journal read\` reads yours, \`${b} journal read --name <peer>\` reads a peer's.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harnery",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Multi-agent coordination for AI coding agents - Claude Code, Cursor, and Codex.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ryan Kelly",
|
|
@@ -132,6 +132,16 @@
|
|
|
132
132
|
"types": "./dist/core/semantic/index.d.ts",
|
|
133
133
|
"import": "./dist/core/semantic/index.js"
|
|
134
134
|
},
|
|
135
|
+
"./core/supervisor": {
|
|
136
|
+
"bun": "./src/core/supervisor/index.ts",
|
|
137
|
+
"types": "./dist/core/supervisor/index.d.ts",
|
|
138
|
+
"import": "./dist/core/supervisor/index.js"
|
|
139
|
+
},
|
|
140
|
+
"./core/diagnostics": {
|
|
141
|
+
"bun": "./src/core/diagnostics/index.ts",
|
|
142
|
+
"types": "./dist/core/diagnostics/index.d.ts",
|
|
143
|
+
"import": "./dist/core/diagnostics/index.js"
|
|
144
|
+
},
|
|
135
145
|
"./core/governor/plans": {
|
|
136
146
|
"bun": "./src/core/governor/plan-read.ts",
|
|
137
147
|
"types": "./dist/core/governor/plan-read.d.ts",
|
|
@@ -230,6 +240,7 @@
|
|
|
230
240
|
"check:portability": "bun run scripts/check-portability.ts",
|
|
231
241
|
"check:public-surface": "bun run scripts/check-public-surface.ts",
|
|
232
242
|
"check:layering": "bun run scripts/check-layering.ts",
|
|
243
|
+
"probe:supervisor-budget": "bun run scripts/probe-supervisor-budget.ts",
|
|
233
244
|
"lint:fix": "bun x @biomejs/biome check --write src",
|
|
234
245
|
"format": "bun x @biomejs/biome format --write src",
|
|
235
246
|
"test": "bun run scripts/test.mjs",
|
package/src/commander.ts
CHANGED
|
@@ -440,6 +440,12 @@ function harneryCommandBundles({
|
|
|
440
440
|
),
|
|
441
441
|
{ hasOptions: true },
|
|
442
442
|
),
|
|
443
|
+
lazy(
|
|
444
|
+
"qa-run <target>",
|
|
445
|
+
`Run the whole page-QA matrix in one command (planner, deterministic gates per context, interactions, critique, snapshot) via child \`${binName} browse\` processes; writes page-qa-result.json with a fail-closed verdict.`,
|
|
446
|
+
async (program) => (await import("./commands/qa-run.ts")).registerQaRunCommand(program, emit),
|
|
447
|
+
{ hasOptions: true },
|
|
448
|
+
),
|
|
443
449
|
lazy(
|
|
444
450
|
"browse-session",
|
|
445
451
|
"Control an opted-in headed browse session through an owner-only descriptor",
|
|
@@ -509,6 +515,26 @@ function harneryCommandBundles({
|
|
|
509
515
|
async (program) =>
|
|
510
516
|
(await import("./commands/semantic.ts")).registerSemanticCommand(program, emit, context),
|
|
511
517
|
),
|
|
518
|
+
lazy(
|
|
519
|
+
"resources",
|
|
520
|
+
"Observe local CPU and memory pressure by agent and process tree",
|
|
521
|
+
async (program) =>
|
|
522
|
+
(await import("./commands/resources.ts")).registerResourcesCommand(program, emit, context),
|
|
523
|
+
),
|
|
524
|
+
lazy("supervisor", "Run Harnery's optional local diagnostic supervisor", async (program) =>
|
|
525
|
+
(await import("./commands/supervisor.ts")).registerSupervisorCommand(program, emit, context),
|
|
526
|
+
),
|
|
527
|
+
lazy(
|
|
528
|
+
"diagnostics",
|
|
529
|
+
"Inspect, capture, and replay bounded local diagnostic findings",
|
|
530
|
+
async (program) =>
|
|
531
|
+
(await import("./commands/diagnostics.ts")).registerDiagnosticsCommand(
|
|
532
|
+
program,
|
|
533
|
+
emit,
|
|
534
|
+
context,
|
|
535
|
+
),
|
|
536
|
+
{ hasOptions: true },
|
|
537
|
+
),
|
|
512
538
|
lazy(
|
|
513
539
|
"storage",
|
|
514
540
|
"Inspect registered Harnery storage without reading file bodies",
|
package/src/commands/agents.ts
CHANGED
|
@@ -49,6 +49,14 @@ import {
|
|
|
49
49
|
LiveCoordinationAuthorityV3Error,
|
|
50
50
|
reopenLiveCoordinationGenerationV3,
|
|
51
51
|
} from "../core/agents/live-authority-v3.ts";
|
|
52
|
+
import {
|
|
53
|
+
isAddressableName,
|
|
54
|
+
MailboxCapacityError,
|
|
55
|
+
type QueueResult,
|
|
56
|
+
queueMailboxMessage,
|
|
57
|
+
suggestNames,
|
|
58
|
+
} from "../core/agents/mailbox.ts";
|
|
59
|
+
import { reconcileCoordinationV3 } from "../core/agents/reconcile-coordination-v3.ts";
|
|
52
60
|
import {
|
|
53
61
|
listSessionFinalizationRequestsV3,
|
|
54
62
|
observeHostDisappearedV3,
|
|
@@ -435,7 +443,8 @@ export function registerAgentsCommand(
|
|
|
435
443
|
cmd
|
|
436
444
|
.command("reconcile")
|
|
437
445
|
.description(
|
|
438
|
-
"
|
|
446
|
+
"Sweep the stale coordination cache, then reconcile archive, idle, parent/run " +
|
|
447
|
+
"completion, stale, superseded, and host lifecycle signals into V3 session finalization.",
|
|
439
448
|
)
|
|
440
449
|
.option("--watch", "Keep reconciling until interrupted")
|
|
441
450
|
.option("--interval-seconds <n>", "Watch interval in seconds")
|
|
@@ -485,9 +494,10 @@ export function registerAgentsCommand(
|
|
|
485
494
|
cmd
|
|
486
495
|
.command("ping <name> <message...>")
|
|
487
496
|
.description(
|
|
488
|
-
"
|
|
489
|
-
"
|
|
490
|
-
"
|
|
497
|
+
"Send a message to another agent by name. Works whether or not that agent " +
|
|
498
|
+
"is running: a live peer sees it on their next prompt, and a name with no " +
|
|
499
|
+
"live session gets it queued until a session of that name next starts. " +
|
|
500
|
+
"Only an unknown name is refused.",
|
|
491
501
|
)
|
|
492
502
|
.option("--json", "JSON output")
|
|
493
503
|
.action((name: string, message: string[], opts: { json?: boolean }) => {
|
|
@@ -546,7 +556,7 @@ export function registerAgentsCommand(
|
|
|
546
556
|
.command("health")
|
|
547
557
|
.description(
|
|
548
558
|
"One-screen coord-layer health rollup: heal events, council activity, " +
|
|
549
|
-
"
|
|
559
|
+
"heartbeat cache issues, and findings. Designed for " +
|
|
550
560
|
"daily glance + dashboard ingestion. Reads .harnery/.",
|
|
551
561
|
)
|
|
552
562
|
.option("--since <window>", "Window (Nh | Nd). Default: 24h.", "24h")
|
|
@@ -747,7 +757,7 @@ async function runSessionReconcile(opts: {
|
|
|
747
757
|
}
|
|
748
758
|
let stopped = false;
|
|
749
759
|
do {
|
|
750
|
-
emit.data(
|
|
760
|
+
emit.data(reconcileCoordinationV3(root));
|
|
751
761
|
if (!opts.watch || stopped) return;
|
|
752
762
|
await new Promise<void>((resolvePromise) => {
|
|
753
763
|
const timer = setTimeout(resolvePromise, interval * 1_000);
|
|
@@ -3022,7 +3032,7 @@ interface HealthReport {
|
|
|
3022
3032
|
// Heartbeat cache files present in active/ but stale or malformed. These are
|
|
3023
3033
|
// files the sweep is not catching; a parseable old heartbeat is stale, not
|
|
3024
3034
|
// evidence of an epoch-like or corrupt timestamp.
|
|
3025
|
-
|
|
3035
|
+
heartbeat_cache_issues: {
|
|
3026
3036
|
count: number;
|
|
3027
3037
|
samples: string[];
|
|
3028
3038
|
};
|
|
@@ -3038,7 +3048,7 @@ interface HealthReport {
|
|
|
3038
3048
|
latest_at: string | null;
|
|
3039
3049
|
sessions: string[];
|
|
3040
3050
|
};
|
|
3041
|
-
|
|
3051
|
+
findings: string[];
|
|
3042
3052
|
}
|
|
3043
3053
|
|
|
3044
3054
|
/** Open-span soft watermark per producer state. The producer-state reader
|
|
@@ -3946,8 +3956,8 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
3946
3956
|
// Heartbeat cache issues: files in active/ that are unparseable, nameless,
|
|
3947
3957
|
// invalidly timestamped, or stale. Old parseable timestamps are ordinary
|
|
3948
3958
|
// staleness, not evidence of epoch-like timestamp corruption.
|
|
3949
|
-
let
|
|
3950
|
-
const
|
|
3959
|
+
let cacheIssueCount = 0;
|
|
3960
|
+
const cacheIssueSamples: string[] = [];
|
|
3951
3961
|
const STALE_HEARTBEAT_AGE_MS = 24 * 60 * 60 * 1000;
|
|
3952
3962
|
if (existsSync(activeDir)) {
|
|
3953
3963
|
for (const file of readdirSync(activeDir)) {
|
|
@@ -3960,16 +3970,16 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
3960
3970
|
hb = null;
|
|
3961
3971
|
}
|
|
3962
3972
|
if (!hb || typeof hb.instance_id !== "string") {
|
|
3963
|
-
|
|
3964
|
-
if (
|
|
3965
|
-
|
|
3973
|
+
cacheIssueCount++;
|
|
3974
|
+
if (cacheIssueSamples.length < 5)
|
|
3975
|
+
cacheIssueSamples.push(`${idFromFile.slice(0, 12)} (unparseable/no-id)`);
|
|
3966
3976
|
continue;
|
|
3967
3977
|
}
|
|
3968
3978
|
const cacheIssue = classifyHeartbeatCacheIssue(hb, nowMs, STALE_HEARTBEAT_AGE_MS);
|
|
3969
3979
|
if (cacheIssue) {
|
|
3970
|
-
|
|
3971
|
-
if (
|
|
3972
|
-
|
|
3980
|
+
cacheIssueCount++;
|
|
3981
|
+
if (cacheIssueSamples.length < 5) {
|
|
3982
|
+
cacheIssueSamples.push(`${idFromFile.slice(0, 12)} (${cacheIssue})`);
|
|
3973
3983
|
}
|
|
3974
3984
|
}
|
|
3975
3985
|
}
|
|
@@ -3985,11 +3995,11 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
3985
3995
|
}
|
|
3986
3996
|
}
|
|
3987
3997
|
|
|
3988
|
-
//
|
|
3989
|
-
const
|
|
3998
|
+
// Finding detection.
|
|
3999
|
+
const findings: string[] = [];
|
|
3990
4000
|
for (const { agent, count } of healTopAgents) {
|
|
3991
4001
|
if (count >= 5) {
|
|
3992
|
-
|
|
4002
|
+
findings.push(
|
|
3993
4003
|
`${agent} self-healed ${count}x in ${opts.since}; possible idle-prune loop or PID instability`,
|
|
3994
4004
|
);
|
|
3995
4005
|
}
|
|
@@ -4000,7 +4010,7 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
4000
4010
|
activeAgents.source === "event-ledger-v3"
|
|
4001
4011
|
? "lifecycle reconciliation may be delayed"
|
|
4002
4012
|
: "heal mechanism may not be firing";
|
|
4003
|
-
|
|
4013
|
+
findings.push(
|
|
4004
4014
|
`${activeAgents.stale} active ${noun}(s) without activity for ${Math.floor(freshnessCutoffSecs() / 60)}min; ${action}`,
|
|
4005
4015
|
);
|
|
4006
4016
|
}
|
|
@@ -4009,31 +4019,33 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
4009
4019
|
(schema) => schema !== expectedSchema,
|
|
4010
4020
|
);
|
|
4011
4021
|
if (unexpectedSchemas.length > 0) {
|
|
4012
|
-
|
|
4022
|
+
findings.push(
|
|
4013
4023
|
`Unexpected active-agent schema versions in use: ${unexpectedSchemas.join(", ")} (expected ${expectedSchema})`,
|
|
4014
4024
|
);
|
|
4015
4025
|
}
|
|
4016
|
-
// Only recent hook failures are active
|
|
4026
|
+
// Only recent hook failures are active findings. Historical rows remain in
|
|
4017
4027
|
// the report with exact error signatures and their latest timestamp.
|
|
4018
4028
|
if (hookErrors.last1h > 0) {
|
|
4019
4029
|
const top = hookErrors.recentTopErrors[0];
|
|
4020
4030
|
const detail = top ? `: top '${top.error.slice(0, 80)}' x${top.count} (${top.phase})` : "";
|
|
4021
|
-
|
|
4031
|
+
findings.push(`agent-hook errored ${hookErrors.last1h}x in the last hour${detail}`);
|
|
4022
4032
|
}
|
|
4023
4033
|
if (stream.cursor_backlog > 500) {
|
|
4024
|
-
|
|
4034
|
+
findings.push(
|
|
4025
4035
|
`projection cursor is ${stream.cursor_backlog} events behind; drain lagging (stop projection may be failing)`,
|
|
4026
4036
|
);
|
|
4027
4037
|
}
|
|
4028
|
-
// Raw V3 stream size is not
|
|
4038
|
+
// Raw V3 stream size is not a finding; catalog rotation bounds readers.
|
|
4029
4039
|
if ((sweptByReason.unparseable ?? 0) > 0) {
|
|
4030
|
-
|
|
4040
|
+
findings.push(
|
|
4031
4041
|
`${sweptByReason.unparseable} heartbeat(s) swept as unparseable in ${opts.since}; possible corruption or a non-atomic writer`,
|
|
4032
4042
|
);
|
|
4033
4043
|
}
|
|
4034
|
-
if (
|
|
4035
|
-
|
|
4036
|
-
`${
|
|
4044
|
+
if (cacheIssueCount > 0) {
|
|
4045
|
+
findings.push(
|
|
4046
|
+
`${cacheIssueCount} heartbeat cache issue(s) in active/ (${cacheIssueSamples.join(", ")}); ` +
|
|
4047
|
+
"rows the last sweep deliberately kept (malformed but mtime-fresh, or neither audit " +
|
|
4048
|
+
"write could persist), or that aged past the freshness cutoff since it ran",
|
|
4037
4049
|
);
|
|
4038
4050
|
}
|
|
4039
4051
|
if (stopRemediation.total > 0) {
|
|
@@ -4041,7 +4053,7 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
4041
4053
|
stopRemediation.sessions.length > 0
|
|
4042
4054
|
? ` (sessions: ${stopRemediation.sessions.map((s) => s.slice(0, 8)).join(", ")})`
|
|
4043
4055
|
: "";
|
|
4044
|
-
|
|
4056
|
+
findings.push(
|
|
4045
4057
|
`stop-hook remediation cap exhausted ${stopRemediation.total}x in ${opts.since}${who}; these sessions ended turns without the end-of-turn ritual — check why their evidence never lands`,
|
|
4046
4058
|
);
|
|
4047
4059
|
}
|
|
@@ -4056,17 +4068,17 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
4056
4068
|
(sum, generation) => sum + generation.span_count,
|
|
4057
4069
|
0,
|
|
4058
4070
|
);
|
|
4059
|
-
|
|
4071
|
+
findings.push(
|
|
4060
4072
|
`${orphanSpans} open tool span(s) across ${orphanGenerations.length} generation(s) with no open turn; orphaned spans block clean session end`,
|
|
4061
4073
|
);
|
|
4062
4074
|
}
|
|
4063
4075
|
for (const pressure of eventLedger.span_pressure) {
|
|
4064
|
-
|
|
4076
|
+
findings.push(
|
|
4065
4077
|
`${pressure.instance_id} (${pressure.generation_id}) holds ${pressure.span_count} open spans (soft watermark ${SPAN_PRESSURE_SOFT_WATERMARK}; the reader's 256-span cap makes the state unreadable)`,
|
|
4066
4078
|
);
|
|
4067
4079
|
}
|
|
4068
4080
|
for (const collectionError of eventLedger.collection_errors) {
|
|
4069
|
-
|
|
4081
|
+
findings.push(`event-ledger health: ${collectionError}`);
|
|
4070
4082
|
}
|
|
4071
4083
|
}
|
|
4072
4084
|
|
|
@@ -4111,14 +4123,14 @@ function runHealth(opts: { since: string; json?: boolean }): void {
|
|
|
4111
4123
|
recent_top_errors: hookErrors.recentTopErrors,
|
|
4112
4124
|
},
|
|
4113
4125
|
stream,
|
|
4114
|
-
|
|
4126
|
+
heartbeat_cache_issues: { count: cacheIssueCount, samples: cacheIssueSamples },
|
|
4115
4127
|
event_ledger: eventLedger,
|
|
4116
4128
|
stop_remediation: {
|
|
4117
4129
|
total: stopRemediation.total,
|
|
4118
4130
|
latest_at: stopRemediation.latestAt,
|
|
4119
4131
|
sessions: stopRemediation.sessions,
|
|
4120
4132
|
},
|
|
4121
|
-
|
|
4133
|
+
findings,
|
|
4122
4134
|
};
|
|
4123
4135
|
|
|
4124
4136
|
if (opts.json) {
|
|
@@ -4211,12 +4223,12 @@ function renderHealthBox(report: HealthReport): void {
|
|
|
4211
4223
|
["event ledger", ledgerStr],
|
|
4212
4224
|
[
|
|
4213
4225
|
"cache issues",
|
|
4214
|
-
report.
|
|
4226
|
+
report.heartbeat_cache_issues.count === 0
|
|
4215
4227
|
? "0"
|
|
4216
|
-
: `${report.
|
|
4228
|
+
: `${report.heartbeat_cache_issues.count} (${report.heartbeat_cache_issues.samples.join(", ")})`,
|
|
4217
4229
|
],
|
|
4218
4230
|
["councils", councilParts.join(", ")],
|
|
4219
|
-
["
|
|
4231
|
+
["findings", report.findings.length === 0 ? "(clean)" : `${report.findings.length} flagged`],
|
|
4220
4232
|
];
|
|
4221
4233
|
|
|
4222
4234
|
const localTime = formatLocalTime(new Date(report.generated_at));
|
|
@@ -4224,9 +4236,9 @@ function renderHealthBox(report: HealthReport): void {
|
|
|
4224
4236
|
|
|
4225
4237
|
process.stdout.write(`${formatBox(title, rows)}\n`); // lint-ok-emission: chat-paste path; mirrors runStatus's direct write so the box surfaces in both TTY + harn-session-teed contexts
|
|
4226
4238
|
|
|
4227
|
-
if (report.
|
|
4239
|
+
if (report.findings.length > 0) {
|
|
4228
4240
|
process.stdout.write("\n"); // lint-ok-emission: same chat-paste path
|
|
4229
|
-
for (const a of report.
|
|
4241
|
+
for (const a of report.findings) {
|
|
4230
4242
|
process.stdout.write(` ! ${a}\n`); // lint-ok-emission: same
|
|
4231
4243
|
}
|
|
4232
4244
|
}
|
|
@@ -4660,18 +4672,52 @@ function runPing(name: string, message: string, opts: { json?: boolean }): void
|
|
|
4660
4672
|
);
|
|
4661
4673
|
process.exit(1);
|
|
4662
4674
|
}
|
|
4675
|
+
const coordRoot = monorepoRoot();
|
|
4663
4676
|
const peerOwner = resolveOwnerByName(name);
|
|
4664
|
-
|
|
4677
|
+
|
|
4678
|
+
// A name with no live session is the common case, not an error: the agent it
|
|
4679
|
+
// refers to may have ended minutes ago and may come back under the same name.
|
|
4680
|
+
// Refuse only a name that no agent has ever held, which is a typo — queueing
|
|
4681
|
+
// that would swallow the message silently.
|
|
4682
|
+
if (!peerOwner && coordRoot && !isAddressableName(coordRoot, name)) {
|
|
4683
|
+
const suggestions = suggestNames(coordRoot, name);
|
|
4665
4684
|
emit.error({
|
|
4666
|
-
code: "
|
|
4667
|
-
message:
|
|
4685
|
+
code: "unknown_agent",
|
|
4686
|
+
message:
|
|
4687
|
+
`no agent has ever been named "${name}". ` +
|
|
4688
|
+
(suggestions.length > 0 ? `Did you mean ${suggestions.join(", ")}? ` : "") +
|
|
4689
|
+
`Run \`${resolveBinName()} agents list\` for live sessions.`,
|
|
4668
4690
|
});
|
|
4669
4691
|
process.exit(1);
|
|
4670
4692
|
}
|
|
4693
|
+
|
|
4671
4694
|
const myHb = readCurrentCoordinationRow(myOwner);
|
|
4672
4695
|
const fromName = myHb?.name ?? "anonymous";
|
|
4673
4696
|
const body = `from agent-${fromName}: ${message.trim()}`;
|
|
4674
|
-
|
|
4697
|
+
|
|
4698
|
+
// A live recipient gets the journal entry immediately, so a peer reading
|
|
4699
|
+
// their own journal mid-session sees it without waiting for a prompt. A
|
|
4700
|
+
// dormant recipient has no journal to write to yet; the drain writes it into
|
|
4701
|
+
// whichever session picks the message up.
|
|
4702
|
+
const doc = peerOwner ? appendEntry(peerOwner, "handoff", body) : null;
|
|
4703
|
+
|
|
4704
|
+
let queued: QueueResult;
|
|
4705
|
+
try {
|
|
4706
|
+
queued = queueMailboxMessage({
|
|
4707
|
+
coordRoot: coordRoot ?? process.cwd(),
|
|
4708
|
+
toName: name,
|
|
4709
|
+
fromName,
|
|
4710
|
+
fromInstanceId: myOwner,
|
|
4711
|
+
body: message.trim(),
|
|
4712
|
+
journaled: Boolean(peerOwner),
|
|
4713
|
+
});
|
|
4714
|
+
} catch (error) {
|
|
4715
|
+
if (error instanceof MailboxCapacityError) {
|
|
4716
|
+
emit.error({ code: `mailbox_${error.reason_code}`, message: error.message });
|
|
4717
|
+
process.exit(1);
|
|
4718
|
+
}
|
|
4719
|
+
throw error;
|
|
4720
|
+
}
|
|
4675
4721
|
|
|
4676
4722
|
// Canonical delivery record: sender is the envelope owner, recipient rides
|
|
4677
4723
|
// in data, so read-only observers can render the communication without
|
|
@@ -4683,18 +4729,21 @@ function runPing(name: string, message: string, opts: { json?: boolean }): void
|
|
|
4683
4729
|
observation: {
|
|
4684
4730
|
event_type: "coord.message_observed",
|
|
4685
4731
|
direction: "sent",
|
|
4686
|
-
subject: peerOwner
|
|
4732
|
+
subject: peerOwner ?? `name:${queued.message.to_name}`,
|
|
4687
4733
|
body: message.trim(),
|
|
4688
4734
|
},
|
|
4689
4735
|
});
|
|
4690
4736
|
|
|
4691
4737
|
const data = {
|
|
4692
|
-
peer:
|
|
4738
|
+
peer: queued.message.to_name,
|
|
4693
4739
|
peer_instance_id: peerOwner,
|
|
4740
|
+
delivery: peerOwner ? ("live" as const) : ("queued" as const),
|
|
4741
|
+
message_id: queued.message.message_id,
|
|
4742
|
+
pending: queued.pending,
|
|
4694
4743
|
from: fromName,
|
|
4695
4744
|
body,
|
|
4696
|
-
journal_path: doc
|
|
4697
|
-
journal_bytes: doc
|
|
4745
|
+
journal_path: doc?.path ?? null,
|
|
4746
|
+
journal_bytes: doc?.bytes ?? null,
|
|
4698
4747
|
};
|
|
4699
4748
|
|
|
4700
4749
|
if (opts.json) {
|
|
@@ -4703,7 +4752,11 @@ function runPing(name: string, message: string, opts: { json?: boolean }): void
|
|
|
4703
4752
|
return;
|
|
4704
4753
|
}
|
|
4705
4754
|
emit.data(data);
|
|
4706
|
-
emit.text(
|
|
4755
|
+
emit.text(
|
|
4756
|
+
peerOwner
|
|
4757
|
+
? `delivered to agent-${data.peer} (live; they see it on their next prompt): "${truncate(message.trim(), 80)}"\n`
|
|
4758
|
+
: `queued for agent-${data.peer} (no live session; delivered when a session of that name next starts): "${truncate(message.trim(), 80)}"\n`,
|
|
4759
|
+
);
|
|
4707
4760
|
}
|
|
4708
4761
|
|
|
4709
4762
|
async function runWait(
|