@lannguyensi/harness 0.10.0 → 0.11.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 +94 -0
- package/README.md +1 -1
- package/dist/cli/approve/understanding.d.ts +8 -0
- package/dist/cli/approve/understanding.js +56 -7
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/audit.d.ts +8 -0
- package/dist/cli/audit.js +2 -2
- package/dist/cli/audit.js.map +1 -1
- package/dist/cli/doctor/format.js +7 -1
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.js +62 -5
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/types.d.ts +15 -0
- package/dist/cli/dry-run.js +9 -3
- package/dist/cli/dry-run.js.map +1 -1
- package/dist/cli/explain.d.ts +8 -0
- package/dist/cli/explain.js +6 -4
- package/dist/cli/explain.js.map +1 -1
- package/dist/cli/index.js +40 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/dependencies.js +17 -7
- package/dist/cli/init/dependencies.js.map +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +14 -5
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.d.ts +9 -0
- package/dist/cli/pack/hook-pre-tool-use.js +88 -2
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/policy/intercept.d.ts +7 -1
- package/dist/cli/policy/intercept.js +28 -6
- package/dist/cli/policy/intercept.js.map +1 -1
- package/dist/cli/session-start/index.d.ts +52 -0
- package/dist/cli/session-start/index.js +195 -0
- package/dist/cli/session-start/index.js.map +1 -0
- package/dist/runtime/git-context.d.ts +16 -0
- package/dist/runtime/git-context.js +97 -0
- package/dist/runtime/git-context.js.map +1 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/pending-approval.d.ts +31 -0
- package/dist/runtime/pending-approval.js +80 -0
- package/dist/runtime/pending-approval.js.map +1 -0
- package/dist/runtime/session-id.d.ts +40 -1
- package/dist/runtime/session-id.js +99 -8
- package/dist/runtime/session-id.js.map +1 -1
- package/package.json +1 -1
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
// (Phase 4 #6) surfaces the runtime audit trail when configured.
|
|
23
23
|
import { queryLedgerByTag, } from "../../policies/index.js";
|
|
24
24
|
import { checkPersistedReport, defaultReportsDir, matchLedgerEntries, } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
|
|
25
|
+
import { resolveGeneratedDir, writePendingApproval, } from "../../runtime/pending-approval.js";
|
|
25
26
|
import { loadManifest } from "../loader.js";
|
|
26
27
|
const PACK_NAME = "understanding-before-execution";
|
|
27
28
|
async function readStdin(stream) {
|
|
@@ -57,6 +58,38 @@ function blockJson(toolName, reason) {
|
|
|
57
58
|
},
|
|
58
59
|
});
|
|
59
60
|
}
|
|
61
|
+
function isEscapeCommand(command) {
|
|
62
|
+
// The operator-approval command `harness approve ...`. The Understanding
|
|
63
|
+
// Gate must not hard-deny it: a `deny` gives no interactive prompt, so
|
|
64
|
+
// denying the very command that records the operator's approval makes the
|
|
65
|
+
// gate un-recoverable from inside the session. Deliberately strict: the
|
|
66
|
+
// command must BE a `harness approve` invocation, with no shell chaining,
|
|
67
|
+
// substitution, or redirection, so the allowlist cannot be used to smuggle
|
|
68
|
+
// other work past the gate.
|
|
69
|
+
const trimmed = command.trim();
|
|
70
|
+
if (/[;&|\n<>]/.test(trimmed))
|
|
71
|
+
return false;
|
|
72
|
+
if (trimmed.includes("`") || trimmed.includes("$("))
|
|
73
|
+
return false;
|
|
74
|
+
return /^harness\s+approve\b/.test(trimmed);
|
|
75
|
+
}
|
|
76
|
+
// The Claude Code PreToolUse "ask" envelope: surface the normal interactive
|
|
77
|
+
// permission prompt. Per the hooks contract `permissionDecision: "ask"` is
|
|
78
|
+
// PreToolUse-only, and the legacy top-level `decision` field is omitted on
|
|
79
|
+
// purpose: a `decision: "block"` would hard-block legacy 2.0.x CLIs and
|
|
80
|
+
// defeat the ask.
|
|
81
|
+
function askJson() {
|
|
82
|
+
const reason = "Understanding Gate: no approved Understanding Report yet. This is a " +
|
|
83
|
+
"`harness approve` command (the operator-approval path). Approve this " +
|
|
84
|
+
"prompt to record your go.";
|
|
85
|
+
return JSON.stringify({
|
|
86
|
+
hookSpecificOutput: {
|
|
87
|
+
hookEventName: "PreToolUse",
|
|
88
|
+
permissionDecision: "ask",
|
|
89
|
+
permissionDecisionReason: reason,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
}
|
|
60
93
|
async function checkLedger(manifest, sessionId, opts) {
|
|
61
94
|
if (opts.ledgerQuery) {
|
|
62
95
|
const result = await opts.ledgerQuery(sessionId);
|
|
@@ -104,11 +137,26 @@ export async function runPackHookPreToolUseCli(opts = {}) {
|
|
|
104
137
|
process.env.CLAUDE_SESSION_ID ??
|
|
105
138
|
"";
|
|
106
139
|
const toolName = typeof event.tool_name === "string" ? event.tool_name : "(unknown)";
|
|
140
|
+
const rawCommand = event.tool_input && typeof event.tool_input === "object"
|
|
141
|
+
? event.tool_input.command
|
|
142
|
+
: undefined;
|
|
143
|
+
const commandStr = typeof rawCommand === "string" ? rawCommand : "";
|
|
107
144
|
// Load manifest (or use injection). Bail to allow on any failure so a
|
|
108
|
-
// missing harness install never bricks the session.
|
|
145
|
+
// missing harness install never bricks the session. The resolved
|
|
146
|
+
// manifest path feeds the harness.generated/ lookup below; an injected
|
|
147
|
+
// manifest has no path, so the staging write is skipped in that case
|
|
148
|
+
// (tests inject `generatedDir` directly instead).
|
|
109
149
|
let manifest;
|
|
150
|
+
let manifestPath;
|
|
110
151
|
try {
|
|
111
|
-
|
|
152
|
+
if (opts.manifest) {
|
|
153
|
+
manifest = opts.manifest;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
const loaded = loadManifest(opts);
|
|
157
|
+
manifest = loaded.manifest;
|
|
158
|
+
manifestPath = loaded.resolved.base;
|
|
159
|
+
}
|
|
112
160
|
}
|
|
113
161
|
catch (err) {
|
|
114
162
|
const diagnostic = `harness pack hook: manifest load failed (${err.message}), allowing.`;
|
|
@@ -181,6 +229,44 @@ export async function runPackHookPreToolUseCli(opts = {}) {
|
|
|
181
229
|
}
|
|
182
230
|
// Neither source approved.
|
|
183
231
|
const reason = `${ledger.detail}; ${report.detail}`;
|
|
232
|
+
// Stage the session id so `harness approve`, run from the operator's
|
|
233
|
+
// shell where $CLAUDE_SESSION_ID is unset, can resolve it without
|
|
234
|
+
// guessing from transcript filenames. Covers both the ask and the
|
|
235
|
+
// block branches below. Best-effort: a staging-write failure must not
|
|
236
|
+
// escalate a gate block into a hook error.
|
|
237
|
+
const generatedDir = opts.generatedDir ??
|
|
238
|
+
(manifestPath !== undefined
|
|
239
|
+
? resolveGeneratedDir({
|
|
240
|
+
...(opts.homeDir !== undefined ? { homeDir: opts.homeDir } : {}),
|
|
241
|
+
manifestPath,
|
|
242
|
+
})
|
|
243
|
+
: undefined);
|
|
244
|
+
if (generatedDir !== undefined) {
|
|
245
|
+
try {
|
|
246
|
+
writePendingApproval(generatedDir, sessionId);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
/* best-effort; the ask / block below proceeds regardless */
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Exception: the operator-approval command itself. Hard-denying
|
|
253
|
+
// `harness approve understanding` is a catch-22: it is the very command
|
|
254
|
+
// that records the operator's go, and a Bash `deny` gives no prompt to
|
|
255
|
+
// approve. Defer it to the interactive permission prompt instead, so the
|
|
256
|
+
// operator's go on that prompt IS the approval, and `harness approve
|
|
257
|
+
// understanding` then writes the ledger tag that unblocks the session.
|
|
258
|
+
if (toolName === "Bash" && isEscapeCommand(commandStr)) {
|
|
259
|
+
const diagnostic = `harness pack hook: ASK: operator-approval command, deferring to the interactive permission prompt`;
|
|
260
|
+
stderr.write(`${diagnostic}\n`);
|
|
261
|
+
stdout.write(`${askJson()}\n`);
|
|
262
|
+
return {
|
|
263
|
+
exitCode: 0,
|
|
264
|
+
blocked: false,
|
|
265
|
+
asked: true,
|
|
266
|
+
approvalCheck: { approved: false, source: "none", detail: reason },
|
|
267
|
+
diagnostic,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
184
270
|
const diagnostic = `harness pack hook: BLOCK — ${reason}`;
|
|
185
271
|
stderr.write(`${diagnostic}\n`);
|
|
186
272
|
stdout.write(`${blockJson(toolName, "no approved Understanding Report for this session")}\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-pre-tool-use.js","sourceRoot":"","sources":["../../../src/cli/pack/hook-pre-tool-use.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,yDAAyD;AACzD,wEAAwE;AACxE,qEAAqE;AACrE,kEAAkE;AAClE,yEAAyE;AACzE,2CAA2C;AAC3C,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,oEAAoE;AACpE,sEAAsE;AACtE,sEAAsE;AACtE,sEAAsE;AACtE,EAAE;AACF,iEAAiE;AACjE,wEAAwE;AACxE,qEAAqE;AACrE,oEAAoE;AACpE,qEAAqE;AACrE,iEAAiE;AAEjE,OAAO,EACL,gBAAgB,GAEjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GAEnB,MAAM,sEAAsE,CAAC;
|
|
1
|
+
{"version":3,"file":"hook-pre-tool-use.js","sourceRoot":"","sources":["../../../src/cli/pack/hook-pre-tool-use.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type LedgerEntry } from "../../policies/index.js";
|
|
2
2
|
import { type LedgerClient, type PolicyDecision } from "../../runtime/index.js";
|
|
3
|
-
import type { Manifest } from "../../schema/index.js";
|
|
3
|
+
import type { Manifest, McpServer } from "../../schema/index.js";
|
|
4
4
|
import { type LoaderOptions } from "../loader.js";
|
|
5
5
|
export interface InterceptCliOptions extends LoaderOptions {
|
|
6
6
|
/** Defaults to process.stdin. */
|
|
@@ -30,5 +30,11 @@ export interface InterceptCliResult {
|
|
|
30
30
|
decisions: PolicyDecision[];
|
|
31
31
|
blocked: boolean;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Real grounding-mcp-backed ledger client. Exported for testing: the
|
|
35
|
+
* `record` adapter's failure-surfacing path is exercised against a
|
|
36
|
+
* bogus mcpCommand in tests/runtime/intercept-cli.test.ts.
|
|
37
|
+
*/
|
|
38
|
+
export declare function realLedgerClient(server: McpServer, opts: InterceptCliOptions): LedgerClient;
|
|
33
39
|
export declare function runInterceptCli(opts?: InterceptCliOptions): Promise<InterceptCliResult>;
|
|
34
40
|
export type { LedgerEntry };
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// the PreToolUse hook. Reads the event JSON from stdin, runs the runtime
|
|
5
5
|
// interceptor, writes Claude Code's deny JSON to stdout on block.
|
|
6
6
|
import { queryLedgerByTag, } from "../../policies/index.js";
|
|
7
|
-
import { intercept, recordPolicyDecision, } from "../../runtime/index.js";
|
|
7
|
+
import { intercept, recordPolicyDecision, resolveGitContext, } from "../../runtime/index.js";
|
|
8
8
|
import { loadManifest } from "../loader.js";
|
|
9
9
|
async function readStdin(stream) {
|
|
10
10
|
return new Promise((resolve, reject) => {
|
|
@@ -55,12 +55,18 @@ function isVerboseEnabled(opts) {
|
|
|
55
55
|
// Accept anything truthy except literal disable-words (case-insensitive).
|
|
56
56
|
return !/^(0|false|no|off)$/i.test(env.trim());
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Real grounding-mcp-backed ledger client. Exported for testing: the
|
|
60
|
+
* `record` adapter's failure-surfacing path is exercised against a
|
|
61
|
+
* bogus mcpCommand in tests/runtime/intercept-cli.test.ts.
|
|
62
|
+
*/
|
|
63
|
+
export function realLedgerClient(server, opts) {
|
|
59
64
|
const command = Array.isArray(server.command)
|
|
60
65
|
? server.command
|
|
61
66
|
: server.command.trim().split(/\s+/);
|
|
62
67
|
const env = server.env ?? undefined;
|
|
63
68
|
const timeoutMs = opts.ledgerTimeoutMs ?? server.health?.timeout_ms ?? 5_000;
|
|
69
|
+
const stderr = opts.stderr ?? process.stderr;
|
|
64
70
|
return {
|
|
65
71
|
async query(_tag, sessionId) {
|
|
66
72
|
return queryLedgerByTag({
|
|
@@ -71,11 +77,19 @@ function realLedgerClient(server, opts) {
|
|
|
71
77
|
});
|
|
72
78
|
},
|
|
73
79
|
async record(decision, sessionId) {
|
|
74
|
-
await recordPolicyDecision(decision, sessionId, {
|
|
80
|
+
const result = await recordPolicyDecision(decision, sessionId, {
|
|
75
81
|
mcpCommand: command,
|
|
76
82
|
...(env && { mcpEnv: env }),
|
|
77
83
|
timeoutMs,
|
|
78
84
|
});
|
|
85
|
+
// A failed audit-write must not block the tool — the decision is
|
|
86
|
+
// still applied — but it must not be silent either: an unrecorded
|
|
87
|
+
// decision is invisible to `harness audit` / `explain --trace`.
|
|
88
|
+
// Goes to stderr so Claude Code's stdout deny-JSON contract holds.
|
|
89
|
+
if (!result.ok) {
|
|
90
|
+
stderr.write(`harness policy intercept: audit-write failed for ` +
|
|
91
|
+
`${decision.policyName}: ${result.reason ?? "unknown error"}\n`);
|
|
92
|
+
}
|
|
79
93
|
},
|
|
80
94
|
};
|
|
81
95
|
}
|
|
@@ -138,12 +152,20 @@ export async function runInterceptCli(opts = {}) {
|
|
|
138
152
|
// a stripped event can pick up the active session.
|
|
139
153
|
const eventSessionId = typeof event.session_id === "string" ? event.session_id : undefined;
|
|
140
154
|
const builtinSessionId = eventSessionId ?? process.env.CLAUDE_SESSION_ID ?? "";
|
|
155
|
+
// REPO / BRANCH are derived from the tool event's cwd so that per-repo
|
|
156
|
+
// and per-branch ledger tags (`preflight:${REPO}`, `preflight:${BRANCH}`)
|
|
157
|
+
// actually namespace — they were previously read from HARNESS_REPO /
|
|
158
|
+
// HARNESS_BRANCH env vars that nothing sets, collapsing every tag to the
|
|
159
|
+
// literal `preflight:`. An explicit env var still wins: it is the
|
|
160
|
+
// operator's deliberate override of the derived value.
|
|
161
|
+
const cwd = typeof event.cwd === "string" ? event.cwd : process.cwd();
|
|
162
|
+
const gitContext = resolveGitContext(cwd);
|
|
141
163
|
const builtins = {
|
|
142
164
|
SESSION_ID: builtinSessionId,
|
|
143
|
-
REPO: process.env.HARNESS_REPO ??
|
|
144
|
-
BRANCH: process.env.HARNESS_BRANCH ??
|
|
165
|
+
REPO: process.env.HARNESS_REPO ?? gitContext.repo,
|
|
166
|
+
BRANCH: process.env.HARNESS_BRANCH ?? gitContext.branch,
|
|
145
167
|
TOOL_NAME: typeof event.tool_name === "string" ? event.tool_name : "",
|
|
146
|
-
CWD:
|
|
168
|
+
CWD: cwd,
|
|
147
169
|
};
|
|
148
170
|
const result = await intercept({
|
|
149
171
|
manifest,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intercept.js","sourceRoot":"","sources":["../../../src/cli/policy/intercept.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,kEAAkE;AAElE,OAAO,EACL,gBAAgB,GAGjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,SAAS,EACT,oBAAoB,
|
|
1
|
+
{"version":3,"file":"intercept.js","sourceRoot":"","sources":["../../../src/cli/policy/intercept.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type LoaderOptions } from "../loader.js";
|
|
2
|
+
/** The slice of `preflight run --json` output this producer reads. */
|
|
3
|
+
export interface PreflightJson {
|
|
4
|
+
ready?: boolean;
|
|
5
|
+
confidence?: number;
|
|
6
|
+
checks?: Array<{
|
|
7
|
+
name?: string;
|
|
8
|
+
status?: string;
|
|
9
|
+
message?: string;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export type RunPreflightResult = {
|
|
13
|
+
ok: true;
|
|
14
|
+
json: PreflightJson;
|
|
15
|
+
} | {
|
|
16
|
+
ok: false;
|
|
17
|
+
reason: string;
|
|
18
|
+
};
|
|
19
|
+
export interface SessionStartPreflightOptions extends LoaderOptions {
|
|
20
|
+
/** Defaults to process.stdin. */
|
|
21
|
+
stdin?: NodeJS.ReadableStream;
|
|
22
|
+
/** Defaults to process.stderr. stdout is never written (SessionStart). */
|
|
23
|
+
stderr?: NodeJS.WritableStream;
|
|
24
|
+
/** `preflight` subprocess timeout in ms. */
|
|
25
|
+
preflightTimeoutMs?: number;
|
|
26
|
+
/** Per-call ledger timeout in ms. */
|
|
27
|
+
ledgerTimeoutMs?: number;
|
|
28
|
+
/** Inject the preflight runner (tests). */
|
|
29
|
+
runPreflight?: (cwd: string, timeoutMs: number) => Promise<RunPreflightResult>;
|
|
30
|
+
/** Inject the ledger writer (tests). */
|
|
31
|
+
writeLedger?: (args: {
|
|
32
|
+
sessionId: string;
|
|
33
|
+
content: string;
|
|
34
|
+
source: string;
|
|
35
|
+
}) => Promise<{
|
|
36
|
+
ok: boolean;
|
|
37
|
+
reason?: string;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
export interface SessionStartPreflightResult {
|
|
41
|
+
/** Always 0 — a SessionStart hook must never break the session loop. */
|
|
42
|
+
exitCode: number;
|
|
43
|
+
/** Whether the `preflight:` ledger fact was written. */
|
|
44
|
+
wrote: boolean;
|
|
45
|
+
/** Resolved repo name (the `${REPO}` a tag is namespaced by). */
|
|
46
|
+
repo: string;
|
|
47
|
+
/** Resolved branch (the `${BRANCH}` a tag is namespaced by; "" if detached). */
|
|
48
|
+
branch: string;
|
|
49
|
+
/** Human-readable explanation of a non-write outcome, for diagnostics. */
|
|
50
|
+
reason?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function runSessionStartPreflight(opts?: SessionStartPreflightOptions): Promise<SessionStartPreflightResult>;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// `harness session-start preflight` — SessionStart hook entrypoint.
|
|
2
|
+
//
|
|
3
|
+
// Wired by the Full template's `git-preflight` SessionStart hook. Reads
|
|
4
|
+
// the SessionStart event JSON from stdin, runs `agent-preflight`
|
|
5
|
+
// (`preflight run --json <cwd>`), and on a `ready:true` result writes a
|
|
6
|
+
// `preflight:${REPO}` fact to the evidence ledger so the
|
|
7
|
+
// `preflight-before-investigation` / `preflight-before-push` policies
|
|
8
|
+
// have a fresh tag to match within their `within` windows.
|
|
9
|
+
//
|
|
10
|
+
// SessionStart hooks are `blocking:false`: this command MUST NOT break
|
|
11
|
+
// the session loop. Every failure path — `preflight` not on PATH, a
|
|
12
|
+
// timeout, a non-`ready` result, an unreachable ledger — logs one line
|
|
13
|
+
// to stderr and exits 0. The only observable effect of a failure is
|
|
14
|
+
// that the preflight policies stay closed (which is the safe default).
|
|
15
|
+
//
|
|
16
|
+
// `ready:false` deliberately does NOT write the tag: the policy intent
|
|
17
|
+
// is "block investigative git reads until agent-preflight ran cleanly",
|
|
18
|
+
// so a failing preflight must leave the gate shut, not satisfy it.
|
|
19
|
+
import { execFile } from "node:child_process";
|
|
20
|
+
import { addLedgerFact, resolveGitContext, resolveSessionId, } from "../../runtime/index.js";
|
|
21
|
+
import { loadManifest } from "../loader.js";
|
|
22
|
+
const PREFLIGHT_BIN = "preflight";
|
|
23
|
+
const DEFAULT_PREFLIGHT_TIMEOUT_MS = 25_000;
|
|
24
|
+
const LEDGER_SOURCE = "harness-session-start-preflight";
|
|
25
|
+
async function readStdin(stream) {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
let data = "";
|
|
28
|
+
stream.setEncoding("utf8");
|
|
29
|
+
stream.on("data", (chunk) => {
|
|
30
|
+
data += chunk;
|
|
31
|
+
});
|
|
32
|
+
stream.on("end", () => resolve(data));
|
|
33
|
+
stream.on("error", reject);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function findGroundingMcp(manifest) {
|
|
37
|
+
return manifest.tools.mcp.find((m) => m.name === "grounding-mcp") ?? null;
|
|
38
|
+
}
|
|
39
|
+
function mcpCommandList(server) {
|
|
40
|
+
return Array.isArray(server.command)
|
|
41
|
+
? server.command
|
|
42
|
+
: server.command.trim().split(/\s+/);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Default `preflight` runner: spawn `preflight run --json <cwd>` and
|
|
46
|
+
* parse its stdout. Resolves `{ ok: false }` (never throws) for the
|
|
47
|
+
* not-installed / timeout / unparseable cases so the caller can degrade.
|
|
48
|
+
*/
|
|
49
|
+
function spawnPreflight(cwd, timeoutMs) {
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
execFile(PREFLIGHT_BIN, ["run", "--json", cwd], { timeout: timeoutMs, maxBuffer: 16 * 1024 * 1024, encoding: "utf8" }, (err, stdout) => {
|
|
52
|
+
// `preflight` may exit non-zero on a not-ready result while still
|
|
53
|
+
// emitting valid JSON, so a parseable stdout wins over the exit
|
|
54
|
+
// code. Only a missing binary / timeout / unparseable output is a
|
|
55
|
+
// genuine "could not run".
|
|
56
|
+
const text = (stdout ?? "").trim();
|
|
57
|
+
if (text.length > 0) {
|
|
58
|
+
try {
|
|
59
|
+
return resolve({ ok: true, json: JSON.parse(text) });
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
/* fall through to the error path */
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (err) {
|
|
66
|
+
const e = err;
|
|
67
|
+
if (e.code === "ENOENT") {
|
|
68
|
+
return resolve({
|
|
69
|
+
ok: false,
|
|
70
|
+
reason: `\`${PREFLIGHT_BIN}\` not on PATH (npm i -g @lannguyensi/agent-preflight)`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// maxBuffer overflow also sets `killed:true`; check it first so
|
|
74
|
+
// an over-budget output is not mis-reported as a timeout.
|
|
75
|
+
if (e.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
|
|
76
|
+
return resolve({
|
|
77
|
+
ok: false,
|
|
78
|
+
reason: `\`${PREFLIGHT_BIN} run --json\` output exceeded the read buffer`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (e.killed) {
|
|
82
|
+
return resolve({
|
|
83
|
+
ok: false,
|
|
84
|
+
reason: `\`${PREFLIGHT_BIN} run\` timed out after ${timeoutMs}ms`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return resolve({ ok: false, reason: `\`${PREFLIGHT_BIN} run\` failed: ${e.message}` });
|
|
88
|
+
}
|
|
89
|
+
return resolve({
|
|
90
|
+
ok: false,
|
|
91
|
+
reason: `\`${PREFLIGHT_BIN} run --json\` produced no parseable JSON`,
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function describeNotReady(json) {
|
|
97
|
+
const failing = (json.checks ?? [])
|
|
98
|
+
.filter((c) => c.status === "fail" || c.status === "error")
|
|
99
|
+
.map((c) => c.name ?? "(unnamed)");
|
|
100
|
+
const confidence = typeof json.confidence === "number" ? json.confidence.toFixed(2) : "?";
|
|
101
|
+
const failSuffix = failing.length > 0 ? `; failing: ${failing.join(", ")}` : "";
|
|
102
|
+
return `preflight not ready (confidence ${confidence})${failSuffix}`;
|
|
103
|
+
}
|
|
104
|
+
export async function runSessionStartPreflight(opts = {}) {
|
|
105
|
+
const stdin = opts.stdin ?? process.stdin;
|
|
106
|
+
const stderr = opts.stderr ?? process.stderr;
|
|
107
|
+
const preflightTimeoutMs = opts.preflightTimeoutMs ?? DEFAULT_PREFLIGHT_TIMEOUT_MS;
|
|
108
|
+
const note = (msg) => {
|
|
109
|
+
stderr.write(`harness session-start preflight: ${msg}\n`);
|
|
110
|
+
};
|
|
111
|
+
const done = (wrote, repo, branch, reason) => ({
|
|
112
|
+
exitCode: 0,
|
|
113
|
+
wrote,
|
|
114
|
+
repo,
|
|
115
|
+
branch,
|
|
116
|
+
...(reason !== undefined && { reason }),
|
|
117
|
+
});
|
|
118
|
+
let event;
|
|
119
|
+
try {
|
|
120
|
+
event = JSON.parse((await readStdin(stdin)).trim() || "{}");
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
const reason = `malformed event JSON: ${err.message}`;
|
|
124
|
+
note(reason);
|
|
125
|
+
return done(false, "", "", reason);
|
|
126
|
+
}
|
|
127
|
+
const cwd = typeof event.cwd === "string" && event.cwd.length > 0 ? event.cwd : process.cwd();
|
|
128
|
+
const { repo, branch } = resolveGitContext(cwd);
|
|
129
|
+
if (repo === "") {
|
|
130
|
+
const reason = `cwd is not inside a git work tree (${cwd}); nothing to preflight`;
|
|
131
|
+
note(reason);
|
|
132
|
+
return done(false, "", "", reason);
|
|
133
|
+
}
|
|
134
|
+
const sessionId = resolveSessionId(typeof event.session_id === "string" ? event.session_id : undefined);
|
|
135
|
+
const runPreflight = opts.runPreflight ?? spawnPreflight;
|
|
136
|
+
const preflight = await runPreflight(cwd, preflightTimeoutMs);
|
|
137
|
+
if (!preflight.ok) {
|
|
138
|
+
note(preflight.reason);
|
|
139
|
+
return done(false, repo, branch, preflight.reason);
|
|
140
|
+
}
|
|
141
|
+
if (preflight.json.ready !== true) {
|
|
142
|
+
const reason = describeNotReady(preflight.json);
|
|
143
|
+
note(`${reason} — leaving the preflight tag unwritten so the gate stays closed`);
|
|
144
|
+
return done(false, repo, branch, reason);
|
|
145
|
+
}
|
|
146
|
+
const confidence = typeof preflight.json.confidence === "number"
|
|
147
|
+
? preflight.json.confidence.toFixed(2)
|
|
148
|
+
: "?";
|
|
149
|
+
// Emit BOTH per-repo and per-branch tags in one fact: the requires
|
|
150
|
+
// evaluator substring-matches, so a single entry containing
|
|
151
|
+
// `preflight:${REPO}` and `preflight:${BRANCH}` satisfies both
|
|
152
|
+
// `preflight-before-investigation` (REPO, within 1h) and
|
|
153
|
+
// `preflight-before-push` (BRANCH, within 10m). Caveat: a SessionStart
|
|
154
|
+
// producer cannot keep the 10m push window fresh through a long
|
|
155
|
+
// session — a push-time refresh is a separate concern (see task notes).
|
|
156
|
+
// On a detached HEAD `branch` is "" — only the REPO tag is written.
|
|
157
|
+
const tags = branch.length > 0 ? `preflight:${repo} preflight:${branch}` : `preflight:${repo}`;
|
|
158
|
+
const content = `${tags} ready:true confidence:${confidence}`;
|
|
159
|
+
let writeLedger = opts.writeLedger;
|
|
160
|
+
if (!writeLedger) {
|
|
161
|
+
let manifest;
|
|
162
|
+
try {
|
|
163
|
+
manifest = loadManifest(opts).manifest;
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
const reason = `manifest load failed: ${err.message}`;
|
|
167
|
+
note(reason);
|
|
168
|
+
return done(false, repo, branch, reason);
|
|
169
|
+
}
|
|
170
|
+
const server = findGroundingMcp(manifest);
|
|
171
|
+
if (!server) {
|
|
172
|
+
const reason = "grounding-mcp not declared in manifest; cannot record preflight tag";
|
|
173
|
+
note(reason);
|
|
174
|
+
return done(false, repo, branch, reason);
|
|
175
|
+
}
|
|
176
|
+
const command = mcpCommandList(server);
|
|
177
|
+
const env = server.env ?? undefined;
|
|
178
|
+
const timeoutMs = opts.ledgerTimeoutMs ?? server.health?.timeout_ms ?? 5_000;
|
|
179
|
+
writeLedger = (args) => addLedgerFact({
|
|
180
|
+
mcpCommand: command,
|
|
181
|
+
...(env && { mcpEnv: env }),
|
|
182
|
+
timeoutMs,
|
|
183
|
+
...args,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
const result = await writeLedger({ sessionId, content, source: LEDGER_SOURCE });
|
|
187
|
+
if (!result.ok) {
|
|
188
|
+
const reason = `ledger write failed: ${result.reason ?? "unknown error"}`;
|
|
189
|
+
note(reason);
|
|
190
|
+
return done(false, repo, branch, reason);
|
|
191
|
+
}
|
|
192
|
+
note(`recorded ${content} for session ${sessionId}`);
|
|
193
|
+
return done(true, repo, branch);
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/session-start/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -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;
|