@lannguyensi/harness 0.10.1 → 0.12.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +160 -7
  2. package/README.md +99 -58
  3. package/dist/cli/apply/apply.js +19 -3
  4. package/dist/cli/apply/apply.js.map +1 -1
  5. package/dist/cli/approve/understanding.d.ts +8 -0
  6. package/dist/cli/approve/understanding.js +154 -15
  7. package/dist/cli/approve/understanding.js.map +1 -1
  8. package/dist/cli/audit.d.ts +8 -0
  9. package/dist/cli/audit.js +2 -2
  10. package/dist/cli/audit.js.map +1 -1
  11. package/dist/cli/doctor/codex.d.ts +6 -1
  12. package/dist/cli/doctor/codex.js +10 -6
  13. package/dist/cli/doctor/codex.js.map +1 -1
  14. package/dist/cli/doctor/format.js +7 -1
  15. package/dist/cli/doctor/format.js.map +1 -1
  16. package/dist/cli/doctor/index.js +62 -5
  17. package/dist/cli/doctor/index.js.map +1 -1
  18. package/dist/cli/doctor/types.d.ts +15 -0
  19. package/dist/cli/dry-run.js +9 -3
  20. package/dist/cli/dry-run.js.map +1 -1
  21. package/dist/cli/explain.d.ts +8 -0
  22. package/dist/cli/explain.js +6 -4
  23. package/dist/cli/explain.js.map +1 -1
  24. package/dist/cli/gate/disable.d.ts +42 -0
  25. package/dist/cli/gate/disable.js +199 -0
  26. package/dist/cli/gate/disable.js.map +1 -0
  27. package/dist/cli/gate/enable.d.ts +33 -0
  28. package/dist/cli/gate/enable.js +127 -0
  29. package/dist/cli/gate/enable.js.map +1 -0
  30. package/dist/cli/gate/snapshot.d.ts +65 -0
  31. package/dist/cli/gate/snapshot.js +119 -0
  32. package/dist/cli/gate/snapshot.js.map +1 -0
  33. package/dist/cli/index.js +141 -1
  34. package/dist/cli/index.js.map +1 -1
  35. package/dist/cli/init/dependencies.js +17 -7
  36. package/dist/cli/init/dependencies.js.map +1 -1
  37. package/dist/cli/init/templates.d.ts +1 -1
  38. package/dist/cli/init/templates.js +14 -5
  39. package/dist/cli/init/templates.js.map +1 -1
  40. package/dist/cli/pack/hook-pre-tool-use.d.ts +2 -0
  41. package/dist/cli/pack/hook-pre-tool-use.js +34 -2
  42. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  43. package/dist/cli/policy/intercept.d.ts +7 -1
  44. package/dist/cli/policy/intercept.js +28 -6
  45. package/dist/cli/policy/intercept.js.map +1 -1
  46. package/dist/cli/session-start/index.d.ts +75 -0
  47. package/dist/cli/session-start/index.js +232 -0
  48. package/dist/cli/session-start/index.js.map +1 -0
  49. package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +27 -0
  50. package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +32 -0
  51. package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +1 -1
  52. package/dist/policy-packs/builtin/understanding-before-execution.d.ts +16 -1
  53. package/dist/policy-packs/builtin/understanding-before-execution.js +35 -7
  54. package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
  55. package/dist/policy-packs/expand.d.ts +3 -1
  56. package/dist/policy-packs/expand.js +2 -2
  57. package/dist/policy-packs/expand.js.map +1 -1
  58. package/dist/policy-packs/index.d.ts +1 -1
  59. package/dist/policy-packs/index.js.map +1 -1
  60. package/dist/policy-packs/registry.d.ts +2 -1
  61. package/dist/policy-packs/registry.js +2 -2
  62. package/dist/policy-packs/registry.js.map +1 -1
  63. package/dist/runtime/git-context.d.ts +16 -0
  64. package/dist/runtime/git-context.js +97 -0
  65. package/dist/runtime/git-context.js.map +1 -0
  66. package/dist/runtime/index.d.ts +1 -0
  67. package/dist/runtime/index.js +1 -0
  68. package/dist/runtime/index.js.map +1 -1
  69. package/dist/runtime/pending-approval.d.ts +31 -0
  70. package/dist/runtime/pending-approval.js +80 -0
  71. package/dist/runtime/pending-approval.js.map +1 -0
  72. package/dist/runtime/session-id.d.ts +40 -1
  73. package/dist/runtime/session-id.js +99 -8
  74. package/dist/runtime/session-id.js.map +1 -1
  75. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy-packs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,GAGf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,eAAe,EACf,SAAS,EACT,YAAY,GAEb,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAmD,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/policy-packs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAiC,MAAM,aAAa,CAAC;AAC/E,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,GAGf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,cAAc,EACd,eAAe,EACf,SAAS,EACT,YAAY,GAEb,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAmD,MAAM,aAAa,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import type { PolicyPack } from "../schema/index.js";
2
+ import { type ResolvePackOptions } from "./builtin/understanding-before-execution.js";
2
3
  import { type Runtime } from "./runtime.js";
3
4
  import type { PackContribution } from "./types.js";
4
5
  export declare const KNOWN_BUILTIN_PACKS: readonly ["understanding-before-execution"];
@@ -8,4 +9,4 @@ export interface ResolveBuiltinResult {
8
9
  contribution: PackContribution;
9
10
  warnings: string[];
10
11
  }
11
- export declare function resolveBuiltin(pack: PolicyPack, runtime?: Runtime): ResolveBuiltinResult | null;
12
+ export declare function resolveBuiltin(pack: PolicyPack, runtime?: Runtime, opts?: ResolvePackOptions): ResolveBuiltinResult | null;
@@ -9,12 +9,12 @@ export const KNOWN_BUILTIN_PACKS = [UNDERSTANDING_BEFORE_EXECUTION];
9
9
  export function isBuiltinPackName(name) {
10
10
  return KNOWN_BUILTIN_PACKS.includes(name);
11
11
  }
12
- export function resolveBuiltin(pack, runtime = DEFAULT_RUNTIME) {
12
+ export function resolveBuiltin(pack, runtime = DEFAULT_RUNTIME, opts = {}) {
13
13
  if (!isBuiltinPackName(pack.name))
14
14
  return null;
15
15
  switch (pack.name) {
16
16
  case UNDERSTANDING_BEFORE_EXECUTION:
17
- return resolveUnderstandingBeforeExecution(pack, runtime);
17
+ return resolveUnderstandingBeforeExecution(pack, runtime, opts);
18
18
  }
19
19
  }
20
20
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/policy-packs/registry.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,mEAAmE;AAGnE,OAAO,EACL,SAAS,IAAI,8BAA8B,EAC3C,OAAO,IAAI,mCAAmC,GAC/C,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,eAAe,EAAgB,MAAM,cAAc,CAAC;AAG7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,8BAA8B,CAAU,CAAC;AAG7E,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAQ,mBAAyC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC;AAOD,MAAM,UAAU,cAAc,CAC5B,IAAgB,EAChB,UAAmB,eAAe;IAElC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,QAAQ,IAAI,CAAC,IAAuB,EAAE,CAAC;QACrC,KAAK,8BAA8B;YACjC,OAAO,mCAAmC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/policy-packs/registry.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,0EAA0E;AAC1E,yEAAyE;AACzE,mEAAmE;AAGnE,OAAO,EACL,SAAS,IAAI,8BAA8B,EAC3C,OAAO,IAAI,mCAAmC,GAE/C,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,eAAe,EAAgB,MAAM,cAAc,CAAC;AAG7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,8BAA8B,CAAU,CAAC;AAG7E,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAQ,mBAAyC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC;AAOD,MAAM,UAAU,cAAc,CAC5B,IAAgB,EAChB,UAAmB,eAAe,EAClC,OAA2B,EAAE;IAE7B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,QAAQ,IAAI,CAAC,IAAuB,EAAE,CAAC;QACrC,KAAK,8BAA8B;YACjC,OAAO,mCAAmC,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACpE,CAAC;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface GitRepoContext {
2
+ /** Basename of the work-tree root, or "" when `cwd` is not in a repo. */
3
+ repo: string;
4
+ /**
5
+ * Current branch name, or "" when not in a repo or HEAD is detached
6
+ * (a raw SHA — there is no branch to name).
7
+ */
8
+ branch: string;
9
+ }
10
+ /**
11
+ * Resolve `{ repo, branch }` for a working directory. Returns empty
12
+ * strings (never throws) when `cwd` is not inside a git work tree, or
13
+ * when any individual lookup fails — callers treat "" as "unknown" and
14
+ * fall through to their own behaviour.
15
+ */
16
+ export declare function resolveGitContext(cwd: string): GitRepoContext;
@@ -0,0 +1,97 @@
1
+ // Resolves the `REPO` and `BRANCH` policy builtins from a working
2
+ // directory.
3
+ //
4
+ // The intercept engine exposes `${REPO}` / `${BRANCH}` as `ledger_tag`
5
+ // template builtins, but they were only ever populated from the
6
+ // `HARNESS_REPO` / `HARNESS_BRANCH` env vars — which nothing sets — so
7
+ // every `preflight:${REPO}` tag collapsed to the literal `preflight:`.
8
+ // That silently degraded the founding-incident policies to one global
9
+ // tag: a preflight done in repo A satisfied the gate in repo B.
10
+ //
11
+ // This module derives both values from the filesystem, not a `git`
12
+ // subprocess: the intercept hook runs on every Bash / Edit / Write
13
+ // tool call, so the resolution must stay cheap. A bounded walk up the
14
+ // directory tree to find `.git`, plus one small `HEAD` read, is
15
+ // microseconds and spawns no process.
16
+ //
17
+ // It is a deliberate approximation of `git rev-parse`: it reads the
18
+ // work tree's basename and `.git/HEAD` directly and does NOT consult
19
+ // `GIT_DIR` / `GIT_WORK_TREE` / `core.worktree`. For namespacing a
20
+ // ledger tag, the on-disk layout is the right (and more stable)
21
+ // signal; those exotic overrides are out of scope.
22
+ import * as fs from "node:fs";
23
+ import * as path from "node:path";
24
+ const EMPTY = { repo: "", branch: "" };
25
+ // A `.git` *file* (linked worktree / submodule) points at the real git
26
+ // dir: `gitdir: <path>`.
27
+ const GITDIR_RE = /^gitdir:\s*(.+)$/;
28
+ // `.git/HEAD` on a branch: `ref: refs/heads/<branch>`. A detached HEAD
29
+ // holds a raw SHA instead and matches nothing here.
30
+ const HEAD_REF_RE = /^ref:\s*refs\/heads\/(.+)$/;
31
+ /**
32
+ * Walk up from `startDir` looking for a `.git` entry. Handles both the
33
+ * common `.git` directory and the `.git` *file* form used by linked
34
+ * worktrees and submodules. The walk is bounded so a pathologically
35
+ * deep cwd cannot spin.
36
+ */
37
+ function findGitEntry(startDir) {
38
+ let dir = path.resolve(startDir);
39
+ for (let depth = 0; depth < 128; depth++) {
40
+ const dotGit = path.join(dir, ".git");
41
+ let stat;
42
+ try {
43
+ stat = fs.statSync(dotGit);
44
+ }
45
+ catch {
46
+ stat = undefined;
47
+ }
48
+ if (stat?.isDirectory()) {
49
+ return { worktreeRoot: dir, gitDir: dotGit };
50
+ }
51
+ if (stat?.isFile()) {
52
+ let gitDir = "";
53
+ try {
54
+ const match = GITDIR_RE.exec(fs.readFileSync(dotGit, "utf8").trim());
55
+ if (match)
56
+ gitDir = path.resolve(dir, match[1].trim());
57
+ }
58
+ catch {
59
+ /* unreadable `.git` file — leave gitDir empty, repo still resolves */
60
+ }
61
+ return { worktreeRoot: dir, gitDir };
62
+ }
63
+ const parent = path.dirname(dir);
64
+ if (parent === dir)
65
+ return null; // hit the filesystem root
66
+ dir = parent;
67
+ }
68
+ return null;
69
+ }
70
+ /**
71
+ * Resolve `{ repo, branch }` for a working directory. Returns empty
72
+ * strings (never throws) when `cwd` is not inside a git work tree, or
73
+ * when any individual lookup fails — callers treat "" as "unknown" and
74
+ * fall through to their own behaviour.
75
+ */
76
+ export function resolveGitContext(cwd) {
77
+ if (typeof cwd !== "string" || cwd.length === 0)
78
+ return EMPTY;
79
+ const entry = findGitEntry(cwd);
80
+ if (!entry)
81
+ return EMPTY;
82
+ const repo = path.basename(entry.worktreeRoot);
83
+ let branch = "";
84
+ if (entry.gitDir) {
85
+ try {
86
+ const head = fs.readFileSync(path.join(entry.gitDir, "HEAD"), "utf8").trim();
87
+ const match = HEAD_REF_RE.exec(head);
88
+ if (match)
89
+ branch = match[1].trim();
90
+ }
91
+ catch {
92
+ /* unreadable HEAD — branch stays "" */
93
+ }
94
+ }
95
+ return { repo, branch };
96
+ }
97
+ //# sourceMappingURL=git-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-context.js","sourceRoot":"","sources":["../../src/runtime/git-context.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  export { intercept, type ClaudeDenyJson, type InterceptOptions, type InterceptResult, type LedgerClient, type PolicyDecision, type PolicyOutcome, type ToolEvent, } from "./intercept.js";
2
2
  export { recordPolicyDecision, payloadFromDecision, encodeLedgerContent, decodeLedgerContent, decisionSortKey, type LedgerRecordOptions, type PolicyDecisionPayload, } from "./ledger-record.js";
3
3
  export { resolveSessionId } from "./session-id.js";
4
+ export { resolveGitContext, type GitRepoContext } from "./git-context.js";
4
5
  export { addLedgerFact, type AddLedgerFactOptions, type AddLedgerFactResult, } from "./ledger-add.js";
@@ -1,5 +1,6 @@
1
1
  export { intercept, } from "./intercept.js";
2
2
  export { recordPolicyDecision, payloadFromDecision, encodeLedgerContent, decodeLedgerContent, decisionSortKey, } from "./ledger-record.js";
3
3
  export { resolveSessionId } from "./session-id.js";
4
+ export { resolveGitContext } from "./git-context.js";
4
5
  export { addLedgerFact, } from "./ledger-add.js";
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,GAQV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,GAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EACL,aAAa,GAGd,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,GAQV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,GAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAuB,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EACL,aAAa,GAGd,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,31 @@
1
+ export declare const GENERATED_DIRNAME = "harness.generated";
2
+ export declare const PENDING_APPROVAL_BASENAME = ".pending-approval";
3
+ /**
4
+ * Resolve `harness.generated/` the way `harness apply` does: a sibling of
5
+ * the manifest, unless an explicit `homeDir` override is in play (tests,
6
+ * non-default home).
7
+ */
8
+ export declare function resolveGeneratedDir(opts: {
9
+ homeDir?: string;
10
+ manifestPath: string;
11
+ }): string;
12
+ export declare function pendingApprovalPath(generatedDir: string): string;
13
+ /**
14
+ * Producer: stage `sessionId` for a later `harness approve`. `atomicWriteFile`
15
+ * creates `generatedDir` if missing, so a hand-wired hook with no prior
16
+ * apply still benefits. Callers treat this as best-effort — a write
17
+ * failure must never escalate a gate block into a thrown hook error.
18
+ */
19
+ export declare function writePendingApproval(generatedDir: string, sessionId: string): void;
20
+ /**
21
+ * Consumer: read the staged session id, or null when the file is absent,
22
+ * empty, whitespace-only, or unreadable. Trims the trailing newline the
23
+ * producer writes plus any surrounding whitespace.
24
+ */
25
+ export declare function readPendingApproval(generatedDir: string): string | null;
26
+ /**
27
+ * Consumer: drop the staging file once its id has been consumed, so a
28
+ * later arg-less `harness approve` cannot revive a stale session id.
29
+ * Best-effort — a missing file counts as success.
30
+ */
31
+ export declare function clearPendingApproval(generatedDir: string): void;
@@ -0,0 +1,80 @@
1
+ // Task 33abc147 — `.pending-approval` session-id staging file.
2
+ //
3
+ // The understanding-gate PreToolUse hook knows the running session's
4
+ // exact `session_id` (it arrives on the hook event's stdin). `harness
5
+ // approve`, run from the operator's `!`-shell, does NOT: $CLAUDE_SESSION_ID
6
+ // is unset in that shell, and guessing the id from the newest project
7
+ // transcript is a heuristic that breaks on subagent / parallel-session
8
+ // transcripts (the approve error message warns about exactly that).
9
+ //
10
+ // So the producer hands the id off instead of making the consumer guess:
11
+ // on every block / ask the gate hook writes the `session_id` to
12
+ // `<generatedDir>/.pending-approval`, and `harness approve` reads it when
13
+ // no `--session` flag and no `$CLAUDE_SESSION_ID` are given. Deterministic,
14
+ // not a guess.
15
+ //
16
+ // `harness apply` only writes its own known files into harness.generated/
17
+ // (it never wipes the directory), so the staging file survives applies.
18
+ // `harness approve` deletes it after a successful resolve so a later
19
+ // arg-less invocation cannot revive a stale session id.
20
+ import * as fs from "node:fs";
21
+ import * as path from "node:path";
22
+ import { atomicWriteFile } from "../io/atomic-write.js";
23
+ // Mirrors `GENERATED_DIRNAME` in cli/apply/apply.ts — also independently
24
+ // duplicated in cli/pack/remove.ts and cli/diff/since-apply.ts. The
25
+ // constant has no single home yet; consolidating the four copies is a
26
+ // separate cleanup.
27
+ export const GENERATED_DIRNAME = "harness.generated";
28
+ export const PENDING_APPROVAL_BASENAME = ".pending-approval";
29
+ /**
30
+ * Resolve `harness.generated/` the way `harness apply` does: a sibling of
31
+ * the manifest, unless an explicit `homeDir` override is in play (tests,
32
+ * non-default home).
33
+ */
34
+ export function resolveGeneratedDir(opts) {
35
+ if (opts.homeDir !== undefined)
36
+ return path.join(opts.homeDir, GENERATED_DIRNAME);
37
+ return path.join(path.dirname(opts.manifestPath), GENERATED_DIRNAME);
38
+ }
39
+ export function pendingApprovalPath(generatedDir) {
40
+ return path.join(generatedDir, PENDING_APPROVAL_BASENAME);
41
+ }
42
+ /**
43
+ * Producer: stage `sessionId` for a later `harness approve`. `atomicWriteFile`
44
+ * creates `generatedDir` if missing, so a hand-wired hook with no prior
45
+ * apply still benefits. Callers treat this as best-effort — a write
46
+ * failure must never escalate a gate block into a thrown hook error.
47
+ */
48
+ export function writePendingApproval(generatedDir, sessionId) {
49
+ atomicWriteFile(pendingApprovalPath(generatedDir), `${sessionId}\n`);
50
+ }
51
+ /**
52
+ * Consumer: read the staged session id, or null when the file is absent,
53
+ * empty, whitespace-only, or unreadable. Trims the trailing newline the
54
+ * producer writes plus any surrounding whitespace.
55
+ */
56
+ export function readPendingApproval(generatedDir) {
57
+ let raw;
58
+ try {
59
+ raw = fs.readFileSync(pendingApprovalPath(generatedDir), "utf8");
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ const trimmed = raw.trim();
65
+ return trimmed.length > 0 ? trimmed : null;
66
+ }
67
+ /**
68
+ * Consumer: drop the staging file once its id has been consumed, so a
69
+ * later arg-less `harness approve` cannot revive a stale session id.
70
+ * Best-effort — a missing file counts as success.
71
+ */
72
+ export function clearPendingApproval(generatedDir) {
73
+ try {
74
+ fs.rmSync(pendingApprovalPath(generatedDir));
75
+ }
76
+ catch {
77
+ /* already gone (or never written) — nothing to clean up */
78
+ }
79
+ }
80
+ //# sourceMappingURL=pending-approval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-approval.js","sourceRoot":"","sources":["../../src/runtime/pending-approval.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AACtE,4EAA4E;AAC5E,sEAAsE;AACtE,uEAAuE;AACvE,oEAAoE;AACpE,EAAE;AACF,yEAAyE;AACzE,gEAAgE;AAChE,0EAA0E;AAC1E,4EAA4E;AAC5E,eAAe;AACf,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AACxE,qEAAqE;AACrE,wDAAwD;AAExD,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,yEAAyE;AACzE,oEAAoE;AACpE,sEAAsE;AACtE,oBAAoB;AACpB,MAAM,CAAC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAErD,MAAM,CAAC,MAAM,yBAAyB,GAAG,mBAAmB,CAAC;AAE7D;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAGnC;IACC,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;IAClF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,iBAAiB,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,yBAAyB,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,YAAoB,EAAE,SAAiB;IAC1E,eAAe,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,GAAG,SAAS,IAAI,CAAC,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,YAAoB;IACvD,IAAI,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;IAC7D,CAAC;AACH,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Resolve the active grounding session id.
2
+ * Resolve the active grounding session id (WRITE path).
3
3
  *
4
4
  * Empty strings are treated as "not provided" — both for the explicit
5
5
  * argument and for the env var, since `--session ''` from a forgetful
@@ -8,3 +8,42 @@
8
8
  * empty session.
9
9
  */
10
10
  export declare function resolveSessionId(explicit?: string): string;
11
+ export interface DiscoverSessionOptions {
12
+ /** Override the Claude Code projects root (tests). */
13
+ projectsRoot?: string;
14
+ /** Override `$HOME` used to derive the projects root (tests). */
15
+ homeDir?: string;
16
+ }
17
+ /**
18
+ * Discover the current Claude Code session id from the most recently
19
+ * modified transcript JSONL under `~/.claude/projects/<project>/`.
20
+ *
21
+ * Claude Code appends to the active session's transcript live, so the
22
+ * newest-mtime `<uuid>.jsonl` across all project dirs is the running
23
+ * session. Returns `null` when the projects root is absent or holds no
24
+ * transcript files — callers fall through to their own default.
25
+ */
26
+ export declare function discoverNewestSessionId(opts?: DiscoverSessionOptions): string | null;
27
+ export interface ResolveReadSessionOptions extends DiscoverSessionOptions {
28
+ /**
29
+ * Test seam — override the transcript-discovery tier wholesale. The
30
+ * default scans the real `~/.claude/projects` via
31
+ * `discoverNewestSessionId`.
32
+ */
33
+ discover?: (opts: DiscoverSessionOptions) => string | null;
34
+ }
35
+ /**
36
+ * Resolve the session id for the READ path (`audit`,
37
+ * `explain --trace/--last`).
38
+ *
39
+ * Precedence:
40
+ * 1. explicit value (the `--session` flag)
41
+ * 2. `$CLAUDE_SESSION_ID` env
42
+ * 3. newest Claude Code transcript (the live session)
43
+ * 4. literal `"default"`
44
+ *
45
+ * The WRITE path keeps `resolveSessionId`: it always has
46
+ * `event.session_id`, so it never reaches the discovery tier, and a
47
+ * per-hook-event filesystem scan would be wasteful.
48
+ */
49
+ export declare function resolveReadSessionId(explicit?: string, opts?: ResolveReadSessionOptions): string;
@@ -9,16 +9,28 @@
9
9
  // audit/trace even though the writes had landed correctly under the
10
10
  // real UUID.
11
11
  //
12
- // This helper lifts the precedence chain into one place:
13
- // 1. explicit value (from a `--session` flag or the runtime's
14
- // `event.session_id`)
15
- // 2. `$CLAUDE_SESSION_ID` env (set by the Claude Code harness inside
16
- // every session)
17
- // 3. literal `"default"` — preserves prior behaviour for ad-hoc
18
- // invocations outside a Claude Code session.
12
+ // `resolveSessionId` lifts the precedence chain into one place for the
13
+ // WRITE path (`policy intercept`), which always carries a concrete
14
+ // `event.session_id`:
15
+ // 1. explicit value (the runtime's `event.session_id`)
16
+ // 2. `$CLAUDE_SESSION_ID` env
17
+ // 3. literal `"default"`
18
+ //
19
+ // The READ path (`audit`, `explain --trace/--last`) needs more. The
20
+ // Phase 5 #2 fix assumed `$CLAUDE_SESSION_ID` is exported into the
21
+ // session's shell environment — it is not (Claude Code does not put it
22
+ // in the Bash tool env), so tier 2 is inert in practice and the readers
23
+ // still fell through to `"default"`. `resolveReadSessionId` adds a
24
+ // transcript-discovery tier: when no explicit id and no env, it reads
25
+ // the active session id off the newest Claude Code transcript JSONL.
26
+ // That is the programmatic form of the heuristic `harness approve`'s
27
+ // own help text recommends to humans.
28
+ import * as fs from "node:fs";
29
+ import * as os from "node:os";
30
+ import * as path from "node:path";
19
31
  const FALLBACK = "default";
20
32
  /**
21
- * Resolve the active grounding session id.
33
+ * Resolve the active grounding session id (WRITE path).
22
34
  *
23
35
  * Empty strings are treated as "not provided" — both for the explicit
24
36
  * argument and for the env var, since `--session ''` from a forgetful
@@ -34,4 +46,83 @@ export function resolveSessionId(explicit) {
34
46
  return env;
35
47
  return FALLBACK;
36
48
  }
49
+ // Claude Code names each session's transcript `<uuid>.jsonl`. Anchored
50
+ // so sibling files (memory dirs, `.pending-approval`, etc.) are ignored.
51
+ const SESSION_TRANSCRIPT_RE = /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
52
+ /**
53
+ * Discover the current Claude Code session id from the most recently
54
+ * modified transcript JSONL under `~/.claude/projects/<project>/`.
55
+ *
56
+ * Claude Code appends to the active session's transcript live, so the
57
+ * newest-mtime `<uuid>.jsonl` across all project dirs is the running
58
+ * session. Returns `null` when the projects root is absent or holds no
59
+ * transcript files — callers fall through to their own default.
60
+ */
61
+ export function discoverNewestSessionId(opts = {}) {
62
+ const projectsRoot = opts.projectsRoot ??
63
+ path.join(opts.homeDir ?? os.homedir(), ".claude", "projects");
64
+ let projectDirs;
65
+ try {
66
+ projectDirs = fs.readdirSync(projectsRoot);
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ let newest = null;
72
+ for (const dir of projectDirs) {
73
+ const projectPath = path.join(projectsRoot, dir);
74
+ let files;
75
+ try {
76
+ files = fs.readdirSync(projectPath);
77
+ }
78
+ catch {
79
+ continue; // not a directory, or unreadable — skip
80
+ }
81
+ for (const file of files) {
82
+ const match = SESSION_TRANSCRIPT_RE.exec(file);
83
+ if (!match)
84
+ continue;
85
+ let mtimeMs;
86
+ try {
87
+ mtimeMs = fs.statSync(path.join(projectPath, file)).mtimeMs;
88
+ }
89
+ catch {
90
+ continue;
91
+ }
92
+ if (newest === null || mtimeMs > newest.mtimeMs) {
93
+ newest = { id: match[1], mtimeMs };
94
+ }
95
+ }
96
+ }
97
+ return newest === null ? null : newest.id;
98
+ }
99
+ /**
100
+ * Resolve the session id for the READ path (`audit`,
101
+ * `explain --trace/--last`).
102
+ *
103
+ * Precedence:
104
+ * 1. explicit value (the `--session` flag)
105
+ * 2. `$CLAUDE_SESSION_ID` env
106
+ * 3. newest Claude Code transcript (the live session)
107
+ * 4. literal `"default"`
108
+ *
109
+ * The WRITE path keeps `resolveSessionId`: it always has
110
+ * `event.session_id`, so it never reaches the discovery tier, and a
111
+ * per-hook-event filesystem scan would be wasteful.
112
+ */
113
+ export function resolveReadSessionId(explicit, opts = {}) {
114
+ if (typeof explicit === "string" && explicit.length > 0)
115
+ return explicit;
116
+ const env = process.env.CLAUDE_SESSION_ID;
117
+ if (typeof env === "string" && env.length > 0)
118
+ return env;
119
+ const discover = opts.discover ?? discoverNewestSessionId;
120
+ const discovered = discover({
121
+ ...(opts.projectsRoot !== undefined && { projectsRoot: opts.projectsRoot }),
122
+ ...(opts.homeDir !== undefined && { homeDir: opts.homeDir }),
123
+ });
124
+ if (typeof discovered === "string" && discovered.length > 0)
125
+ return discovered;
126
+ return FALLBACK;
127
+ }
37
128
  //# sourceMappingURL=session-id.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-id.js","sourceRoot":"","sources":["../../src/runtime/session-id.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,EAAE;AACF,uEAAuE;AACvE,iEAAiE;AACjE,qEAAqE;AACrE,8DAA8D;AAC9D,wEAAwE;AACxE,iEAAiE;AACjE,oEAAoE;AACpE,aAAa;AACb,EAAE;AACF,yDAAyD;AACzD,gEAAgE;AAChE,2BAA2B;AAC3B,uEAAuE;AACvE,sBAAsB;AACtB,kEAAkE;AAClE,kDAAkD;AAElD,MAAM,QAAQ,GAAG,SAAS,CAAC;AAE3B;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAiB;IAChD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACzE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC1C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC1D,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"session-id.js","sourceRoot":"","sources":["../../src/runtime/session-id.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lannguyensi/harness",
3
- "version": "0.10.1",
3
+ "version": "0.12.0",
4
4
  "description": "Declarative control plane for agent harnesses — one YAML for grounding, tools, memory, and hooks.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/LanNguyenSi/harness",