@lannguyensi/harness 0.51.0 → 0.53.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/CHANGELOG.md +182 -0
- package/dist/cli/approve/understanding.d.ts +46 -0
- package/dist/cli/approve/understanding.js +50 -22
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/audit.d.ts +29 -0
- package/dist/cli/audit.js +130 -17
- package/dist/cli/audit.js.map +1 -1
- package/dist/cli/delegate/index.d.ts +63 -0
- package/dist/cli/delegate/index.js +313 -0
- package/dist/cli/delegate/index.js.map +1 -0
- package/dist/cli/doctor/auto-approve-mode.d.ts +37 -0
- package/dist/cli/doctor/auto-approve-mode.js +80 -0
- package/dist/cli/doctor/auto-approve-mode.js.map +1 -0
- package/dist/cli/doctor/bypass-without-auto-approve.d.ts +38 -0
- package/dist/cli/doctor/bypass-without-auto-approve.js +89 -0
- package/dist/cli/doctor/bypass-without-auto-approve.js.map +1 -0
- package/dist/cli/doctor/codex-config-drift.d.ts +28 -0
- package/dist/cli/doctor/codex-config-drift.js +240 -0
- package/dist/cli/doctor/codex-config-drift.js.map +1 -0
- package/dist/cli/doctor/format.js +39 -1
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.js +68 -7
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/settings-drift.d.ts +6 -0
- package/dist/cli/doctor/settings-drift.js +20 -6
- package/dist/cli/doctor/settings-drift.js.map +1 -1
- package/dist/cli/doctor/types.d.ts +54 -0
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/doctor/ug-auto-approvals.js +18 -43
- package/dist/cli/doctor/ug-auto-approvals.js.map +1 -1
- package/dist/cli/doctor/ug-delegations.d.ts +26 -0
- package/dist/cli/doctor/ug-delegations.js +112 -0
- package/dist/cli/doctor/ug-delegations.js.map +1 -0
- package/dist/cli/doctor/understanding-mode-env.d.ts +2 -0
- package/dist/cli/doctor/understanding-mode-env.js +5 -2
- package/dist/cli/doctor/understanding-mode-env.js.map +1 -1
- package/dist/cli/gc/index.d.ts +13 -2
- package/dist/cli/gc/index.js +203 -1
- package/dist/cli/gc/index.js.map +1 -1
- package/dist/cli/index.js +133 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/composer.js +9 -0
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +2 -2
- package/dist/cli/init/profiles.js +11 -0
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +11 -0
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/approve-escape.js +16 -7
- package/dist/cli/pack/approve-escape.js.map +1 -1
- package/dist/cli/pack/auto-approve-path.d.ts +17 -0
- package/dist/cli/pack/auto-approve-path.js +56 -3
- package/dist/cli/pack/auto-approve-path.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.d.ts +25 -0
- package/dist/cli/pack/hook-pre-tool-use.js +335 -3
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/index.d.ts +1 -0
- package/dist/cli/pack/index.js +1 -0
- package/dist/cli/pack/index.js.map +1 -1
- package/dist/cli/pack/transcript-report-scan.d.ts +71 -0
- package/dist/cli/pack/transcript-report-scan.js +359 -0
- package/dist/cli/pack/transcript-report-scan.js.map +1 -0
- package/dist/cli/pack/upgrade.d.ts +32 -0
- package/dist/cli/pack/upgrade.js +273 -0
- package/dist/cli/pack/upgrade.js.map +1 -0
- package/dist/cli/smoke/index.d.ts +18 -1
- package/dist/cli/smoke/index.js +54 -0
- package/dist/cli/smoke/index.js.map +1 -1
- package/dist/io/read-json-dir-entries.d.ts +36 -0
- package/dist/io/read-json-dir-entries.js +38 -0
- package/dist/io/read-json-dir-entries.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.d.ts +35 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js +86 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.d.ts +76 -2
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.js +139 -2
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.d.ts +237 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +647 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +4 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js +26 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.d.ts +71 -0
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js +169 -0
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution.d.ts +19 -0
- package/dist/policy-packs/builtin/understanding-before-execution.js +29 -0
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/dist/runtime/approval-signing.js +28 -0
- package/dist/runtime/approval-signing.js.map +1 -1
- package/dist/runtime/git-context.js +7 -1
- package/dist/runtime/git-context.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -27,7 +27,7 @@ import { add } from "./add/index.js";
|
|
|
27
27
|
import { adopt } from "./adopt/index.js";
|
|
28
28
|
import { apply, CODEX_CONFIG_BASENAME, DRIFT_HINT_MESSAGE, formatNextSteps, OPENCODE_CONFIG_BASENAME, SETTINGS_BASENAME, } from "./apply/index.js";
|
|
29
29
|
import { isRemoveType, KNOWN_REMOVE_TYPES, remove } from "./remove/index.js";
|
|
30
|
-
import { packAdd, packList, packRemove, packReseed } from "./pack/index.js";
|
|
30
|
+
import { packAdd, packList, packRemove, packReseed, packUpgrade } from "./pack/index.js";
|
|
31
31
|
import { runPackHookPreToolUseCli } from "./pack/hook-pre-tool-use.js";
|
|
32
32
|
import { runPackHookPostToolUseCli } from "./pack/hook-post-tool-use.js";
|
|
33
33
|
import { runPackHookTrackActiveClaimCli } from "./pack/hook-track-active-claim.js";
|
|
@@ -41,6 +41,8 @@ import { approveBranchProtection } from "./approve/branch-protection.js";
|
|
|
41
41
|
import { approveRisk } from "./approve/risk.js";
|
|
42
42
|
import { approveUnderstanding } from "./approve/understanding.js";
|
|
43
43
|
import { readPipedStdin } from "./approve/stdin-report.js";
|
|
44
|
+
import { issueDelegation } from "./delegate/index.js";
|
|
45
|
+
import { InvalidDurationError, parseDurationSeconds } from "../policies/index.js";
|
|
44
46
|
import { runRecordDogfood, runRecordReview, runRecordReviewSubagent, } from "./record/index.js";
|
|
45
47
|
import { describe, isPillar } from "./describe.js";
|
|
46
48
|
import { diff as diffRun } from "./diff/index.js";
|
|
@@ -860,6 +862,35 @@ export function buildProgram(opts = {}) {
|
|
|
860
862
|
}
|
|
861
863
|
stdout(`reseeded ${result.fieldsChanged.map((f) => `config.${f}`).join(", ")} for policy_packs entry ${JSON.stringify(result.name)} in ${result.path}\n`);
|
|
862
864
|
});
|
|
865
|
+
// `harness pack upgrade <name>` (task 8f637efd, D-004): text-level
|
|
866
|
+
// insertion of a pack's missing default config block into an existing
|
|
867
|
+
// manifest, idempotent, never rewrites content outside the inserted
|
|
868
|
+
// block. Today's only wired upgrade is `understanding-before-
|
|
869
|
+
// execution`'s `auto_approve` default; see upgrade.ts's module header
|
|
870
|
+
// for why this is text-level rather than a Document-API mutation like
|
|
871
|
+
// `add` / `reseed`.
|
|
872
|
+
packCmd
|
|
873
|
+
.command("upgrade <name>")
|
|
874
|
+
.description("Insert a pack's missing default config block into an existing manifest (today: " +
|
|
875
|
+
"understanding-before-execution's auto_approve default). Idempotent; " +
|
|
876
|
+
"refuses on ambiguity rather than guessing where to insert.")
|
|
877
|
+
.option("--config <path>", "manifest path (default: ~/.harness/harness.yaml; legacy fallback ~/.claude/harness.yaml)")
|
|
878
|
+
.option("--dry-run", "print the unified diff and exit without writing")
|
|
879
|
+
.action(async (name, options) => {
|
|
880
|
+
const result = await packUpgrade(name, {
|
|
881
|
+
configPath: options.config,
|
|
882
|
+
dryRun: options.dryRun,
|
|
883
|
+
});
|
|
884
|
+
if (result.alreadyPresent) {
|
|
885
|
+
stdout(`policy_packs entry ${JSON.stringify(result.name)} already carries this upgrade; nothing to insert (${result.path} unchanged).\n`);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (options.dryRun) {
|
|
889
|
+
stdout(result.diff);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
stdout(`upgraded policy_packs entry ${JSON.stringify(result.name)} in ${result.path}\n`);
|
|
893
|
+
});
|
|
863
894
|
// `harness pack hook` runtime sub-tree (Phase 6 #4): wired by the
|
|
864
895
|
// pack's PreToolUse hook contribution; reads PreToolUse JSON from
|
|
865
896
|
// stdin, consults the signed approval marker (the persisted report and
|
|
@@ -1516,6 +1547,86 @@ export function buildProgram(opts = {}) {
|
|
|
1516
1547
|
}
|
|
1517
1548
|
stdout(`${lines.join("\n")}\n`);
|
|
1518
1549
|
});
|
|
1550
|
+
// `harness delegate` (Slice 3 of docs/decisions/2026-08-27-ug-auto-mode-approval.md,
|
|
1551
|
+
// agent-tasks 37ad0b05): issue a signed pre-authorization for a
|
|
1552
|
+
// headless `claude -p` child session, bound to an already-approved
|
|
1553
|
+
// PARENT session. NOT an approval: the child still writes and gets its
|
|
1554
|
+
// own Understanding Report checked by its own PreToolUse hook before
|
|
1555
|
+
// its auto-marker is minted. Writes harness.generated/.delegations/,
|
|
1556
|
+
// never .approvals/.
|
|
1557
|
+
program
|
|
1558
|
+
.command("delegate")
|
|
1559
|
+
.description("Issue a signed delegation for a headless `claude -p` child session, bound to " +
|
|
1560
|
+
"an already-approved parent session (pre-authorization, not an approval: the " +
|
|
1561
|
+
"child still writes and gets its own Understanding Report checked). Writes " +
|
|
1562
|
+
"harness.generated/.delegations/<child-sid>, never .approvals/. See " +
|
|
1563
|
+
"docs/decisions/2026-08-27-ug-auto-mode-approval.md.")
|
|
1564
|
+
.requiredOption("--child-session <uuid>", "session id of the claude -p child this delegation authorizes (UUID)")
|
|
1565
|
+
.option("--cwd <path>", "bind the delegation to this working directory; at least one of --cwd/--task is required")
|
|
1566
|
+
.option("--task <id>", "bind the delegation to this agent-tasks task id; at least one of --cwd/--task is required")
|
|
1567
|
+
.option("--ttl <duration>", "delegation lifetime (e.g. 30m, 1h); default: the pack's approval_lifecycle.max_age when set, else 1h")
|
|
1568
|
+
.option("--report <path>", "fallback shape: bind the child's Understanding Report by content + path hash at spawn time; not yet consumed by the child hook, see the pack doc")
|
|
1569
|
+
.option("--session-id <id>", "explicit parent session id (default: $CLAUDE_CODE_SESSION_ID, then $CLAUDE_SESSION_ID, then $CODEX_SESSION_ID, then staged .pending-approval)")
|
|
1570
|
+
.option("--config <path>", "manifest path (default: ~/.harness/harness.yaml; legacy fallback ~/.claude/harness.yaml)")
|
|
1571
|
+
.option("--project <name>", "apply per-project overrides")
|
|
1572
|
+
.action(async (options) => {
|
|
1573
|
+
// Usage-level check ahead of every deeper resolution, with the
|
|
1574
|
+
// exact message the ADR slice 3 acceptance criterion pins.
|
|
1575
|
+
// `issueDelegation` carries the identical check (reason
|
|
1576
|
+
// "no-binding") for callers that invoke it directly (the smoke
|
|
1577
|
+
// runner, tests) without going through commander's flag parsing.
|
|
1578
|
+
if (options.cwd === undefined && options.task === undefined) {
|
|
1579
|
+
throw new HarnessExitError("a delegation must bind a cwd or a task", EX_FAIL);
|
|
1580
|
+
}
|
|
1581
|
+
let ttlSeconds;
|
|
1582
|
+
if (options.ttl) {
|
|
1583
|
+
try {
|
|
1584
|
+
ttlSeconds = parseDurationSeconds(options.ttl);
|
|
1585
|
+
}
|
|
1586
|
+
catch (err) {
|
|
1587
|
+
if (err instanceof InvalidDurationError) {
|
|
1588
|
+
throw new HarnessExitError(`--ttl: ${err.message}`, EX_USAGE);
|
|
1589
|
+
}
|
|
1590
|
+
throw err;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
const cliOpts = {
|
|
1594
|
+
childSessionId: options.childSession,
|
|
1595
|
+
};
|
|
1596
|
+
// `!== undefined` (not truthy), agreeing with the usage check
|
|
1597
|
+
// above: a truthy check would silently drop an explicit `--cwd
|
|
1598
|
+
// ''` / `--task ''` instead of letting `issueDelegation` refuse
|
|
1599
|
+
// it with `invalid-cwd` / `invalid-task` (L3 fix, agent-tasks
|
|
1600
|
+
// 37ad0b05).
|
|
1601
|
+
if (options.cwd !== undefined)
|
|
1602
|
+
cliOpts.cwd = options.cwd;
|
|
1603
|
+
if (options.task !== undefined)
|
|
1604
|
+
cliOpts.taskId = options.task;
|
|
1605
|
+
if (ttlSeconds !== undefined)
|
|
1606
|
+
cliOpts.ttlSeconds = ttlSeconds;
|
|
1607
|
+
if (options.report)
|
|
1608
|
+
cliOpts.reportPath = options.report;
|
|
1609
|
+
if (options.sessionId)
|
|
1610
|
+
cliOpts.parentSessionId = options.sessionId;
|
|
1611
|
+
if (options.config)
|
|
1612
|
+
cliOpts.configPath = options.config;
|
|
1613
|
+
if (options.project)
|
|
1614
|
+
cliOpts.project = options.project;
|
|
1615
|
+
const result = await issueDelegation(cliOpts);
|
|
1616
|
+
if (!result.ok) {
|
|
1617
|
+
throw new HarnessExitError(`delegate refused (${result.reason}): ${result.detail}`, EX_FAIL);
|
|
1618
|
+
}
|
|
1619
|
+
const lines = [
|
|
1620
|
+
`delegation: ✓ ${result.filePath} (child ${result.childSessionId}, parent ${result.parentSessionId}, expires ${result.expiresAt})`,
|
|
1621
|
+
];
|
|
1622
|
+
if (result.ledgerFact.written) {
|
|
1623
|
+
lines.push(`ledger: ✓ wrote understanding-delegated:${result.childSessionId}:${result.parentSessionId} (audit only)`);
|
|
1624
|
+
}
|
|
1625
|
+
else {
|
|
1626
|
+
lines.push(`ledger: ⚠ skipped (${result.ledgerFact.reason ?? "unknown"}) (audit only)`);
|
|
1627
|
+
}
|
|
1628
|
+
stdout(`${lines.join("\n")}\n`);
|
|
1629
|
+
});
|
|
1519
1630
|
const VALID_DECISION_FILTERS = [
|
|
1520
1631
|
"allow",
|
|
1521
1632
|
"warn",
|
|
@@ -1641,7 +1752,7 @@ export function buildProgram(opts = {}) {
|
|
|
1641
1752
|
});
|
|
1642
1753
|
program
|
|
1643
1754
|
.command("audit")
|
|
1644
|
-
.description("Replay policy decisions from the evidence ledger for a time window")
|
|
1755
|
+
.description("Replay policy decisions from the evidence ledger for a time window, plus an approvals section listing raw understanding-gate approval facts")
|
|
1645
1756
|
.option("--since <duration>", "time window (default: 24h)")
|
|
1646
1757
|
.option("--policy <name>", "filter to a single policy by name")
|
|
1647
1758
|
.option("--outcome <outcome>", "filter by decision outcome (allow / warn / require_approval / deny / warn-degraded / deny-degraded)")
|
|
@@ -1738,6 +1849,8 @@ export function buildProgram(opts = {}) {
|
|
|
1738
1849
|
.option("--expect-no-hook <names>", "comma-separated list of hook names / events that MUST NOT fire (repeatable)", (value, prev = []) => prev.concat(splitCommaList(value)), [])
|
|
1739
1850
|
.option("--expect-exit <n>", "expected result.is_error: 0 ⇒ false, !=0 ⇒ true")
|
|
1740
1851
|
.option("--expect-decision <kind>", "policy decision must be one of allow|deny|warn")
|
|
1852
|
+
.option("--no-delegate", "do not pre-authorize the spawned child (docs/decisions/2026-08-27-ug-auto-mode-approval.md); " +
|
|
1853
|
+
"it then has no delegation binding it to this approved session and falls back to the plain opt-in path")
|
|
1741
1854
|
.action(async (options) => {
|
|
1742
1855
|
const expectations = {};
|
|
1743
1856
|
if (options.expectHook && options.expectHook.length > 0) {
|
|
@@ -1761,6 +1874,10 @@ export function buildProgram(opts = {}) {
|
|
|
1761
1874
|
outputDir: options.outputDir,
|
|
1762
1875
|
expectations,
|
|
1763
1876
|
};
|
|
1877
|
+
// commander's `--no-delegate` negates `options.delegate` (default
|
|
1878
|
+
// true); only an explicit `--no-delegate` flips it to `false`.
|
|
1879
|
+
if (options.delegate === false)
|
|
1880
|
+
smokeOpts.noDelegate = true;
|
|
1764
1881
|
if (options.config)
|
|
1765
1882
|
smokeOpts.configPath = options.config;
|
|
1766
1883
|
if (options.project)
|
|
@@ -2168,11 +2285,13 @@ export function buildProgram(opts = {}) {
|
|
|
2168
2285
|
program
|
|
2169
2286
|
.command("gc")
|
|
2170
2287
|
.description("Retention-based cleanup of harness-owned gate state: terminal " +
|
|
2171
|
-
"(approved/expired) understanding-gate reports, parse-error logs,
|
|
2172
|
-
"approval markers
|
|
2173
|
-
"
|
|
2174
|
-
"
|
|
2175
|
-
"
|
|
2288
|
+
"(approved/expired) understanding-gate reports, parse-error logs, " +
|
|
2289
|
+
"approval markers, expired delegation markers, orphaned delegation " +
|
|
2290
|
+
"adoption ledgers, and stale permission-mode observations older than " +
|
|
2291
|
+
"the retention window. Pending reports and anything outside the " +
|
|
2292
|
+
"enumerated harness-owned dirs are never touched (the evidence " +
|
|
2293
|
+
"ledger and solution-acceptance verdict dirs are owned by their " +
|
|
2294
|
+
"producers). Dry-run by default; pass --apply to delete.")
|
|
2176
2295
|
.option("--config <path>", "manifest path (default: ~/.harness/harness.yaml; legacy fallback ~/.claude/harness.yaml)")
|
|
2177
2296
|
.option("--retention-days <n>", `delete artifacts older than this many days (default: ${DEFAULT_RETENTION_DAYS})`)
|
|
2178
2297
|
.option("--apply", "delete the listed artifacts (default: dry-run listing only)")
|
|
@@ -2195,10 +2314,17 @@ export function buildProgram(opts = {}) {
|
|
|
2195
2314
|
result.reportsDir,
|
|
2196
2315
|
...(result.parseErrorsDir !== null ? [result.parseErrorsDir] : []),
|
|
2197
2316
|
result.approvalsDir,
|
|
2317
|
+
result.delegationsDir,
|
|
2318
|
+
result.adoptionLedgerDir,
|
|
2319
|
+
result.permissionModeObservationsDir,
|
|
2198
2320
|
];
|
|
2199
2321
|
if (result.parseErrorsDir === null) {
|
|
2200
2322
|
stderr("gc: skipping the parse-errors sweep (reports dir does not have the conventional .understanding-gate/reports shape)\n");
|
|
2201
2323
|
}
|
|
2324
|
+
if (result.unparseable.length > 0) {
|
|
2325
|
+
stderr(`gc: ${result.unparseable.length} delegation file(s) could not be parsed and were left in place:\n` +
|
|
2326
|
+
result.unparseable.map((u) => ` [${u.category}] ${u.filePath} (${u.reason})\n`).join(""));
|
|
2327
|
+
}
|
|
2202
2328
|
if (result.candidates.length === 0) {
|
|
2203
2329
|
stdout(`gc: nothing older than ${result.retentionDays}d (cutoff ${result.cutoffIso}) under\n` +
|
|
2204
2330
|
sweptDirs.map((d) => ` ${d}\n`).join("") +
|