@lannguyensi/harness 0.25.2 → 0.26.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 (60) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cli/event-input.d.ts +28 -0
  3. package/dist/cli/event-input.js +72 -0
  4. package/dist/cli/event-input.js.map +1 -0
  5. package/dist/cli/explain-action.d.ts +20 -0
  6. package/dist/cli/explain-action.js +27 -0
  7. package/dist/cli/explain-action.js.map +1 -0
  8. package/dist/cli/index.js +56 -0
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/pack/hook-branch-protection.js +1 -1
  11. package/dist/cli/pack/hook-branch-protection.js.map +1 -1
  12. package/dist/cli/pack/hook-codex-pre-tool-use.js +1 -1
  13. package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
  14. package/dist/cli/pack/hook-post-tool-use.js +1 -1
  15. package/dist/cli/pack/hook-post-tool-use.js.map +1 -1
  16. package/dist/cli/pack/hook-pre-tool-use.js +1 -1
  17. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  18. package/dist/cli/pack/hook-track-active-claim.js +1 -1
  19. package/dist/cli/pack/hook-track-active-claim.js.map +1 -1
  20. package/dist/cli/{pack/pause-check.d.ts → pause-check.d.ts} +1 -1
  21. package/dist/cli/{pack/pause-check.js → pause-check.js} +14 -11
  22. package/dist/cli/pause-check.js.map +1 -0
  23. package/dist/cli/policy/intercept.d.ts +5 -0
  24. package/dist/cli/policy/intercept.js +21 -0
  25. package/dist/cli/policy/intercept.js.map +1 -1
  26. package/dist/cli/resolve-env.d.ts +32 -0
  27. package/dist/cli/resolve-env.js +47 -0
  28. package/dist/cli/resolve-env.js.map +1 -0
  29. package/dist/cli/test-risk.d.ts +26 -0
  30. package/dist/cli/test-risk.js +34 -0
  31. package/dist/cli/test-risk.js.map +1 -0
  32. package/dist/runtime/action-envelope.d.ts +64 -0
  33. package/dist/runtime/action-envelope.js +46 -0
  34. package/dist/runtime/action-envelope.js.map +1 -0
  35. package/dist/runtime/environment-resolver.d.ts +36 -0
  36. package/dist/runtime/environment-resolver.js +138 -0
  37. package/dist/runtime/environment-resolver.js.map +1 -0
  38. package/dist/runtime/index.d.ts +4 -0
  39. package/dist/runtime/index.js +4 -0
  40. package/dist/runtime/index.js.map +1 -1
  41. package/dist/runtime/kube-context.d.ts +16 -0
  42. package/dist/runtime/kube-context.js +63 -0
  43. package/dist/runtime/kube-context.js.map +1 -0
  44. package/dist/runtime/risk-classifier.d.ts +38 -0
  45. package/dist/runtime/risk-classifier.js +121 -0
  46. package/dist/runtime/risk-classifier.js.map +1 -0
  47. package/dist/schema/environments.d.ts +215 -0
  48. package/dist/schema/environments.js +101 -0
  49. package/dist/schema/environments.js.map +1 -0
  50. package/dist/schema/index.d.ts +408 -0
  51. package/dist/schema/index.js +8 -0
  52. package/dist/schema/index.js.map +1 -1
  53. package/dist/schema/policies.d.ts +139 -0
  54. package/dist/schema/policies.js +39 -0
  55. package/dist/schema/policies.js.map +1 -1
  56. package/dist/schema/risk.d.ts +131 -0
  57. package/dist/schema/risk.js +87 -0
  58. package/dist/schema/risk.js.map +1 -0
  59. package/package.json +1 -1
  60. package/dist/cli/pack/pause-check.js.map +0 -1
@@ -0,0 +1,64 @@
1
+ import type { GitRepoContext } from "./git-context.js";
2
+ import type { ToolEvent } from "./intercept.js";
3
+ export interface ActionEnvelopeSession {
4
+ /** Grounding / Claude Code session id, or "" when absent. */
5
+ id: string;
6
+ /** Work-tree basename for the event's cwd, or "" when not in a repo. */
7
+ repo: string;
8
+ /** Current branch, or "" when not in a repo or HEAD is detached. */
9
+ branch: string;
10
+ /**
11
+ * agent-tasks task id. Not present in the Claude Code PreToolUse
12
+ * payload, so "" in practice; a harness-driven or synthetic event may
13
+ * carry one. The MVP does no agent-tasks lookup.
14
+ */
15
+ task_id: string;
16
+ }
17
+ export interface ActionEnvelopeRuntime {
18
+ /** Working directory the action runs in. */
19
+ cwd: string;
20
+ /** OS user, or "" when unavailable. */
21
+ user: string;
22
+ /** Host name, or "" when unavailable. */
23
+ host: string;
24
+ }
25
+ export interface ActionEnvelope {
26
+ /** Hook event name, e.g. "PreToolUse". "" when absent. */
27
+ event: string;
28
+ /** Tool name, e.g. "Bash". "" when absent. */
29
+ tool: string;
30
+ /** The tool's raw input, verbatim. `null` when the event carries none. */
31
+ raw_input: unknown;
32
+ session: ActionEnvelopeSession;
33
+ runtime: ActionEnvelopeRuntime;
34
+ /** ISO-8601 UTC timestamp the envelope was built. */
35
+ timestamp: string;
36
+ }
37
+ /**
38
+ * Ambient facts the builder cannot derive from the event alone. The CLI
39
+ * wrapper resolves these (filesystem + process reads) and hands them in,
40
+ * keeping `buildActionEnvelope` itself pure and I/O-free — the same
41
+ * resolved-by-the-wrapper pattern `intercept()` uses for the git sha and
42
+ * the policy builtins.
43
+ */
44
+ export interface EnvelopeContext {
45
+ /** Final working directory: the event's cwd, or the wrapper's fallback. */
46
+ cwd: string;
47
+ /** Git context resolved against `cwd`. Empty strings when cwd is not in a repo. */
48
+ git: GitRepoContext;
49
+ /** OS user (`os.userInfo().username`), or "" when unavailable. */
50
+ user: string;
51
+ /** Host name (`os.hostname()`), or "" when unavailable. */
52
+ host: string;
53
+ /** Timestamp to stamp on the envelope. */
54
+ now: Date;
55
+ }
56
+ /**
57
+ * Normalize a runtime tool event into an Action Envelope.
58
+ *
59
+ * Pure: every non-deterministic input (cwd, git, user, host, now)
60
+ * arrives via `context`. A sparse or malformed event never throws —
61
+ * absent fields become "" (or `null` for `raw_input`), so a hand-probed
62
+ * `{}` event still yields a well-formed envelope.
63
+ */
64
+ export declare function buildActionEnvelope(event: ToolEvent, context: EnvelopeContext): ActionEnvelope;
@@ -0,0 +1,46 @@
1
+ // Phase 7 #2 — Action Envelope.
2
+ //
3
+ // The normalized, stable representation of a tool call that the Risk
4
+ // Gate pipeline reasons about. The raw runtime event (`ToolEvent`, the
5
+ // Claude Code PreToolUse hook payload) is runtime-specific and loosely
6
+ // shaped; every downstream Risk Gate stage — the Risk Classifier (#3),
7
+ // Context Resolver (#4), Policy Evaluator (#5) — consumes THIS shape
8
+ // instead, so none of them re-parse a runtime-specific payload.
9
+ //
10
+ // STATUS: built by `harness explain-action` (Phase 7 #2). NOT yet
11
+ // consumed by `harness policy intercept` — routing the runtime through
12
+ // the envelope is Phase 7 #5. See docs/risk-gate.md and docs/ROADMAP.md.
13
+ //
14
+ // Design source: lava-ice-logs/2026-04-30/harness-risk-gate-extension.md
15
+ // ("Action Envelope" section).
16
+ function asString(v) {
17
+ return typeof v === "string" ? v : "";
18
+ }
19
+ /**
20
+ * Normalize a runtime tool event into an Action Envelope.
21
+ *
22
+ * Pure: every non-deterministic input (cwd, git, user, host, now)
23
+ * arrives via `context`. A sparse or malformed event never throws —
24
+ * absent fields become "" (or `null` for `raw_input`), so a hand-probed
25
+ * `{}` event still yields a well-formed envelope.
26
+ */
27
+ export function buildActionEnvelope(event, context) {
28
+ return {
29
+ event: asString(event.hook_event_name),
30
+ tool: asString(event.tool_name),
31
+ raw_input: event.tool_input ?? null,
32
+ session: {
33
+ id: asString(event.session_id),
34
+ repo: context.git.repo,
35
+ branch: context.git.branch,
36
+ task_id: asString(event.task_id),
37
+ },
38
+ runtime: {
39
+ cwd: context.cwd,
40
+ user: context.user,
41
+ host: context.host,
42
+ },
43
+ timestamp: context.now.toISOString(),
44
+ };
45
+ }
46
+ //# sourceMappingURL=action-envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-envelope.js","sourceRoot":"","sources":["../../src/runtime/action-envelope.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,uEAAuE;AACvE,uEAAuE;AACvE,qEAAqE;AACrE,gEAAgE;AAChE,EAAE;AACF,kEAAkE;AAClE,uEAAuE;AACvE,yEAAyE;AACzE,EAAE;AACF,yEAAyE;AACzE,+BAA+B;AA8D/B,SAAS,QAAQ,CAAC,CAAU;IAC1B,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACxC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAgB,EAChB,OAAwB;IAExB,OAAO;QACL,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,eAAe,CAAC;QACtC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;QAC/B,SAAS,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;QACnC,OAAO,EAAE;YACP,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC;YAC9B,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI;YACtB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM;YAC1B,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;SACjC;QACD,OAAO,EAAE;YACP,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB;QACD,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE;KACrC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { EnvironmentResolver, MatchableEnvironment } from "../schema/index.js";
2
+ import type { ActionEnvelope } from "./action-envelope.js";
3
+ export type EnvironmentConfidence = "high" | "medium" | "low";
4
+ export interface EnvironmentResolution {
5
+ /** Resolved environment, or `unknown` when no resolver matched. */
6
+ name: MatchableEnvironment;
7
+ /**
8
+ * `high` when two or more signals (unioned across every resolver
9
+ * asserting the winning environment) back the result, `medium` for a
10
+ * single signal, `low` for `unknown` (no signal matched at all).
11
+ */
12
+ confidence: EnvironmentConfidence;
13
+ /** Matched signal descriptors that back the result, for explainability. */
14
+ signals: string[];
15
+ /** Name of the resolver whose environment won, or `null` when unresolved. */
16
+ resolver: string | null;
17
+ }
18
+ /** Ambient inputs the resolver matches signals against. */
19
+ export interface SignalInputs {
20
+ /** Environment variables, for `env_var_patterns`. */
21
+ env: Record<string, string | undefined>;
22
+ /** Current kube context name, for `kube_context_patterns`. "" when unknown. */
23
+ kubeContext: string;
24
+ /** Current kube namespace, for `kube_namespace_patterns`. "" when unknown. */
25
+ kubeNamespace: string;
26
+ }
27
+ /**
28
+ * Resolve the target environment of an Action Envelope.
29
+ *
30
+ * Pure: envelope + resolvers + ambient inputs in, resolution out, no
31
+ * I/O. When resolvers disagree, the most-dangerous environment wins
32
+ * (`ENV_PRECEDENCE`). Signals from every fired resolver that asserts the
33
+ * winning environment are unioned for explainability. When nothing
34
+ * matches, the result is `unknown` — deliberately not a safe default.
35
+ */
36
+ export declare function resolveEnvironment(envelope: ActionEnvelope, resolvers: readonly EnvironmentResolver[], inputs: SignalInputs): EnvironmentResolution;
@@ -0,0 +1,138 @@
1
+ // Phase 7 #4 — Context Resolver.
2
+ //
3
+ // Classifies an Action Envelope's target environment by matching the
4
+ // manifest's `environments.resolvers[]` signals. The Risk Gate stage
5
+ // that reads the `environments:` schema vocabulary shipped in
6
+ // Phase 7 #1.
7
+ //
8
+ // STATUS: invoked by `harness resolve-env` (Phase 7 #4). NOT yet
9
+ // consumed by `harness policy intercept` — wiring the runtime through
10
+ // the resolver is Phase 7 #5. See docs/risk-gate.md and docs/ROADMAP.md.
11
+ //
12
+ // "Unknown is not safe": when no resolver matches, the environment is
13
+ // `unknown` — not a safe default. The Phase 7 #5 policy evaluator must
14
+ // treat `unknown` as risk-bearing. `MatchableEnvironmentSchema` already
15
+ // admits `unknown` so a `policy.when.environment.name` clause can gate
16
+ // on it.
17
+ //
18
+ // Design source: lava-ice-logs/2026-04-30/harness-risk-gate-extension.md
19
+ // (design phase C).
20
+ // Most-dangerous-wins precedence: when resolvers disagree, the earlier
21
+ // entry here is the resolved environment. `unknown` is the implicit
22
+ // fallback and is not a resolver-assertable value.
23
+ const ENV_PRECEDENCE = [
24
+ "production",
25
+ "staging",
26
+ "dev",
27
+ "local",
28
+ ];
29
+ /**
30
+ * Convert a `*`-glob to an anchored RegExp. Only `*` is special (the
31
+ * Phase 7 #1 signal patterns are simple, e.g. `main`, `release/*`); all
32
+ * other regex metacharacters are escaped literally.
33
+ */
34
+ function globToRegExp(glob) {
35
+ const escaped = glob
36
+ .replace(/[.+^${}()|[\]\\?]/g, "\\$&")
37
+ .replace(/\*/g, ".*");
38
+ return new RegExp(`^${escaped}$`);
39
+ }
40
+ /**
41
+ * Match one resolver's signals against the envelope + ambient inputs.
42
+ * Signals are OR-ed: a resolver fires when ANY signal matches. Returns
43
+ * the matched signal descriptors (empty when the resolver did not fire).
44
+ *
45
+ * Per-signal-kind semantics: `branch_patterns` and
46
+ * `kube_namespace_patterns` are globs, `kube_context_patterns` are
47
+ * regexes, `env_var_patterns` are substrings of the variable's value.
48
+ */
49
+ function matchResolver(resolver, envelope, inputs) {
50
+ const matched = [];
51
+ const sig = resolver.signals;
52
+ const branch = envelope.session.branch;
53
+ if (sig.branch_patterns !== undefined && branch !== "") {
54
+ for (const p of sig.branch_patterns) {
55
+ if (globToRegExp(p).test(branch)) {
56
+ matched.push(`branch:${branch} ~ ${p}`);
57
+ }
58
+ }
59
+ }
60
+ if (sig.env_var_patterns !== undefined) {
61
+ for (const evp of sig.env_var_patterns) {
62
+ const value = inputs.env[evp.var];
63
+ if (typeof value !== "string")
64
+ continue;
65
+ for (const p of evp.patterns) {
66
+ if (value.includes(p)) {
67
+ matched.push(`env:${evp.var} contains "${p}"`);
68
+ }
69
+ }
70
+ }
71
+ }
72
+ if (sig.kube_context_patterns !== undefined && inputs.kubeContext !== "") {
73
+ for (const p of sig.kube_context_patterns) {
74
+ let re;
75
+ try {
76
+ re = new RegExp(p);
77
+ }
78
+ catch {
79
+ continue;
80
+ }
81
+ if (re.test(inputs.kubeContext)) {
82
+ matched.push(`kube-context:${inputs.kubeContext} ~ /${p}/`);
83
+ }
84
+ }
85
+ }
86
+ if (sig.kube_namespace_patterns !== undefined &&
87
+ inputs.kubeNamespace !== "") {
88
+ for (const p of sig.kube_namespace_patterns) {
89
+ if (globToRegExp(p).test(inputs.kubeNamespace)) {
90
+ matched.push(`kube-namespace:${inputs.kubeNamespace} ~ ${p}`);
91
+ }
92
+ }
93
+ }
94
+ return matched;
95
+ }
96
+ /**
97
+ * Resolve the target environment of an Action Envelope.
98
+ *
99
+ * Pure: envelope + resolvers + ambient inputs in, resolution out, no
100
+ * I/O. When resolvers disagree, the most-dangerous environment wins
101
+ * (`ENV_PRECEDENCE`). Signals from every fired resolver that asserts the
102
+ * winning environment are unioned for explainability. When nothing
103
+ * matches, the result is `unknown` — deliberately not a safe default.
104
+ */
105
+ export function resolveEnvironment(envelope, resolvers, inputs) {
106
+ const fired = [];
107
+ for (const resolver of resolvers) {
108
+ const signals = matchResolver(resolver, envelope, inputs);
109
+ if (signals.length > 0)
110
+ fired.push({ resolver, signals });
111
+ }
112
+ if (fired.length === 0) {
113
+ return { name: "unknown", confidence: "low", signals: [], resolver: null };
114
+ }
115
+ let best = fired[0];
116
+ for (const f of fired) {
117
+ if (ENV_PRECEDENCE.indexOf(f.resolver.environment) <
118
+ ENV_PRECEDENCE.indexOf(best.resolver.environment)) {
119
+ best = f;
120
+ }
121
+ }
122
+ // Union the signals of every fired resolver that asserts the winning
123
+ // environment, so the explanation is complete when several resolvers
124
+ // agree. `resolver` names the highest-precedence (first-found) one.
125
+ const winningEnv = best.resolver.environment;
126
+ const signals = [
127
+ ...new Set(fired
128
+ .filter((f) => f.resolver.environment === winningEnv)
129
+ .flatMap((f) => f.signals)),
130
+ ].sort();
131
+ return {
132
+ name: winningEnv,
133
+ confidence: signals.length >= 2 ? "high" : "medium",
134
+ signals,
135
+ resolver: best.resolver.name,
136
+ };
137
+ }
138
+ //# sourceMappingURL=environment-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment-resolver.js","sourceRoot":"","sources":["../../src/runtime/environment-resolver.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,8DAA8D;AAC9D,cAAc;AACd,EAAE;AACF,iEAAiE;AACjE,sEAAsE;AACtE,yEAAyE;AACzE,EAAE;AACF,sEAAsE;AACtE,uEAAuE;AACvE,wEAAwE;AACxE,uEAAuE;AACvE,SAAS;AACT,EAAE;AACF,yEAAyE;AACzE,oBAAoB;AAmCpB,uEAAuE;AACvE,oEAAoE;AACpE,mDAAmD;AACnD,MAAM,cAAc,GAAoC;IACtD,YAAY;IACZ,SAAS;IACT,KAAK;IACL,OAAO;CACR,CAAC;AAEF;;;;GAIG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,OAAO,GAAG,IAAI;SACjB,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC;SACrC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACxB,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC;AACpC,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,aAAa,CACpB,QAA6B,EAC7B,QAAwB,EACxB,MAAoB;IAEpB,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC;IAE7B,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC;IACvC,IAAI,GAAG,CAAC,eAAe,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QACvD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC;YACpC,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBACjC,OAAO,CAAC,IAAI,CAAC,UAAU,MAAM,MAAM,CAAC,EAAE,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QACvC,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,gBAAgB,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,SAAS;YACxC,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;gBAC7B,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;oBACtB,OAAO,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;gBACjD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,GAAG,CAAC,qBAAqB,KAAK,SAAS,IAAI,MAAM,CAAC,WAAW,KAAK,EAAE,EAAE,CAAC;QACzE,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,qBAAqB,EAAE,CAAC;YAC1C,IAAI,EAAU,CAAC;YACf,IAAI,CAAC;gBACH,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;gBAChC,OAAO,CAAC,IAAI,CAAC,gBAAgB,MAAM,CAAC,WAAW,OAAO,CAAC,GAAG,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;IACH,CAAC;IAED,IACE,GAAG,CAAC,uBAAuB,KAAK,SAAS;QACzC,MAAM,CAAC,aAAa,KAAK,EAAE,EAC3B,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,uBAAuB,EAAE,CAAC;YAC5C,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC/C,OAAO,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,aAAa,MAAM,CAAC,EAAE,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAAwB,EACxB,SAAyC,EACzC,MAAoB;IAEpB,MAAM,KAAK,GAAgE,EAAE,CAAC;IAC9E,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC1D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC7E,CAAC;IAED,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IACrB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IACE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;YAC9C,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EACjD,CAAC;YACD,IAAI,GAAG,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IAED,qEAAqE;IACrE,qEAAqE;IACrE,oEAAoE;IACpE,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;IAC7C,MAAM,OAAO,GAAG;QACd,GAAG,IAAI,GAAG,CACR,KAAK;aACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,KAAK,UAAU,CAAC;aACpD,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAC7B;KACF,CAAC,IAAI,EAAE,CAAC;IAET,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;QACnD,OAAO;QACP,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI;KAC7B,CAAC;AACJ,CAAC"}
@@ -3,4 +3,8 @@ export { recordPolicyDecision, payloadFromDecision, encodeLedgerContent, decodeL
3
3
  export { resolveSessionId } from "./session-id.js";
4
4
  export { buildAgentFacingBlock, formatAgentFacingMessage, renderAgentFacing, type AgentFacingBlock, } from "./agent-facing.js";
5
5
  export { resolveGitContext, type GitRepoContext } from "./git-context.js";
6
+ export { buildActionEnvelope, type ActionEnvelope, type ActionEnvelopeRuntime, type ActionEnvelopeSession, type EnvelopeContext, } from "./action-envelope.js";
7
+ export { classifyRisk, type RiskConfidence, type RiskProfile, } from "./risk-classifier.js";
8
+ export { resolveKubeContext, type KubeContext, type ResolveKubeContextOptions, } from "./kube-context.js";
9
+ export { resolveEnvironment, type EnvironmentConfidence, type EnvironmentResolution, type SignalInputs, } from "./environment-resolver.js";
6
10
  export { addLedgerFact, type AddLedgerFactOptions, type AddLedgerFactResult, } from "./ledger-add.js";
@@ -3,5 +3,9 @@ export { recordPolicyDecision, payloadFromDecision, encodeLedgerContent, decodeL
3
3
  export { resolveSessionId } from "./session-id.js";
4
4
  export { buildAgentFacingBlock, formatAgentFacingMessage, renderAgentFacing, } from "./agent-facing.js";
5
5
  export { resolveGitContext } from "./git-context.js";
6
+ export { buildActionEnvelope, } from "./action-envelope.js";
7
+ export { classifyRisk, } from "./risk-classifier.js";
8
+ export { resolveKubeContext, } from "./kube-context.js";
9
+ export { resolveEnvironment, } from "./environment-resolver.js";
6
10
  export { addLedgerFact, } from "./ledger-add.js";
7
11
  //# 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,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,GAElB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAuB,MAAM,kBAAkB,CAAC;AAC1E,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,EACL,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,GAElB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAuB,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EACL,mBAAmB,GAKpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,YAAY,GAGb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,GAGnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,kBAAkB,GAInB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,aAAa,GAGd,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface KubeContext {
2
+ /** Current context name, or "" when unresolved. */
3
+ context: string;
4
+ /** Namespace of the current context, or "" when unresolved. */
5
+ namespace: string;
6
+ }
7
+ export interface ResolveKubeContextOptions {
8
+ /** Override the kubeconfig path (tests). Defaults to `~/.kube/config`. */
9
+ kubeconfigPath?: string;
10
+ }
11
+ /**
12
+ * Resolve `{ context, namespace }` from `~/.kube/config`. Returns empty
13
+ * strings when the file is absent, unparseable, or declares no
14
+ * `current-context`.
15
+ */
16
+ export declare function resolveKubeContext(opts?: ResolveKubeContextOptions): KubeContext;
@@ -0,0 +1,63 @@
1
+ // Resolves the current Kubernetes context + namespace from the
2
+ // standard `~/.kube/config`, for the Phase 7 #4 Context Resolver's
3
+ // `kube_context_patterns` / `kube_namespace_patterns` signals.
4
+ //
5
+ // Like `git-context.ts`, this is a deliberate filesystem approximation:
6
+ // it reads `~/.kube/config` directly and does NOT consult `$KUBECONFIG`
7
+ // file lists or in-cluster service-account state. For classifying a
8
+ // target environment those exotic setups are out of the MVP's scope.
9
+ // Every failure path returns empty strings, never throws — callers
10
+ // treat "" as "unknown".
11
+ import * as fs from "node:fs";
12
+ import * as os from "node:os";
13
+ import * as path from "node:path";
14
+ import { parse as parseYaml } from "yaml";
15
+ const EMPTY = { context: "", namespace: "" };
16
+ /**
17
+ * Resolve `{ context, namespace }` from `~/.kube/config`. Returns empty
18
+ * strings when the file is absent, unparseable, or declares no
19
+ * `current-context`.
20
+ */
21
+ export function resolveKubeContext(opts = {}) {
22
+ const configPath = opts.kubeconfigPath ?? path.join(os.homedir(), ".kube", "config");
23
+ let raw;
24
+ try {
25
+ raw = fs.readFileSync(configPath, "utf8");
26
+ }
27
+ catch {
28
+ return EMPTY;
29
+ }
30
+ let doc;
31
+ try {
32
+ doc = parseYaml(raw);
33
+ }
34
+ catch {
35
+ return EMPTY;
36
+ }
37
+ if (typeof doc !== "object" || doc === null)
38
+ return EMPTY;
39
+ const config = doc;
40
+ const context = typeof config["current-context"] === "string"
41
+ ? config["current-context"]
42
+ : "";
43
+ if (context === "")
44
+ return EMPTY;
45
+ let namespace = "";
46
+ if (Array.isArray(config.contexts)) {
47
+ for (const entry of config.contexts) {
48
+ if (typeof entry !== "object" || entry === null)
49
+ continue;
50
+ const e = entry;
51
+ if (e.name !== context)
52
+ continue;
53
+ if (typeof e.context === "object" && e.context !== null) {
54
+ const ns = e.context.namespace;
55
+ if (typeof ns === "string")
56
+ namespace = ns;
57
+ }
58
+ break;
59
+ }
60
+ }
61
+ return { context, namespace };
62
+ }
63
+ //# sourceMappingURL=kube-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kube-context.js","sourceRoot":"","sources":["../../src/runtime/kube-context.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,mEAAmE;AACnE,+DAA+D;AAC/D,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,oEAAoE;AACpE,qEAAqE;AACrE,mEAAmE;AACnE,yBAAyB;AAEzB,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAS1C,MAAM,KAAK,GAAgB,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAO1D;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAkC,EAAE;IAEpC,MAAM,UAAU,GACd,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAEpE,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAE1D,MAAM,MAAM,GAAG,GAA0D,CAAC;IAC1E,MAAM,OAAO,GACX,OAAO,MAAM,CAAC,iBAAiB,CAAC,KAAK,QAAQ;QAC3C,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC;QAC3B,CAAC,CAAC,EAAE,CAAC;IACT,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAEjC,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;gBAAE,SAAS;YAC1D,MAAM,CAAC,GAAG,KAA8C,CAAC;YACzD,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;gBAAE,SAAS;YACjC,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;gBACxD,MAAM,EAAE,GAAI,CAAC,CAAC,OAAmC,CAAC,SAAS,CAAC;gBAC5D,IAAI,OAAO,EAAE,KAAK,QAAQ;oBAAE,SAAS,GAAG,EAAE,CAAC;YAC7C,CAAC;YACD,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { RiskCategory, RiskClassifier, RiskSeverity } from "../schema/index.js";
2
+ import type { ActionEnvelope } from "./action-envelope.js";
3
+ export type RiskConfidence = "high" | "low";
4
+ export interface RiskProfile {
5
+ /** Did any classifier pattern match the action? */
6
+ classified: boolean;
7
+ /**
8
+ * Highest matched severity, or `null` when unclassified. `null` is
9
+ * NOT "low" — see the module header on "unknown is not safe".
10
+ */
11
+ severity: RiskSeverity | null;
12
+ /** Union of every matched pattern's categories, sorted and deduplicated. */
13
+ categories: RiskCategory[];
14
+ /**
15
+ * `false` when a matched category marks the action irreversible,
16
+ * `true` when classified and nothing marks it irreversible, `null`
17
+ * when unclassified (reversibility is unknown, not assumed).
18
+ */
19
+ reversible: boolean | null;
20
+ /**
21
+ * `high` for any deterministic rule match, `low` when unclassified.
22
+ * A regex classifier has no real probability; the field is a
23
+ * placeholder for the v2 LLM-assisted classifier, where a graded
24
+ * confidence becomes meaningful.
25
+ */
26
+ confidence: RiskConfidence;
27
+ /** One human-readable line per matched pattern, or the no-match note. */
28
+ reasons: string[];
29
+ }
30
+ /**
31
+ * Classify an Action Envelope against the manifest's risk classifiers.
32
+ *
33
+ * Pure: envelope + classifiers in, profile out, no I/O. Multiple
34
+ * matching patterns compose — highest severity wins, categories union,
35
+ * one `reasons` line per hit. An envelope no pattern matches yields the
36
+ * honest unclassified profile (`classified: false`, `severity: null`).
37
+ */
38
+ export declare function classifyRisk(envelope: ActionEnvelope, classifiers: readonly RiskClassifier[]): RiskProfile;
@@ -0,0 +1,121 @@
1
+ // Phase 7 #3 — Risk Classifier.
2
+ //
3
+ // Assigns an Action Envelope a risk profile by regex-matching the
4
+ // manifest's `risk.classifiers[]` against the action. The first Risk
5
+ // Gate stage that reads the `risk:` schema vocabulary shipped in
6
+ // Phase 7 #1.
7
+ //
8
+ // STATUS: invoked by `harness test-risk` (Phase 7 #3). NOT yet consumed
9
+ // by `harness policy intercept` — wiring the runtime through the
10
+ // classifier is Phase 7 #5. See docs/risk-gate.md and docs/ROADMAP.md.
11
+ //
12
+ // "Unknown is not safe": an envelope no pattern matches yields a
13
+ // profile with `classified: false` and `severity: null`, deliberately
14
+ // NOT a low/zero-risk profile. The Phase 7 #5 policy evaluator must
15
+ // treat an unclassified action as risk-bearing rather than allow it by
16
+ // default; this module's job is only to report the unclassified state
17
+ // honestly.
18
+ //
19
+ // Design source: lava-ice-logs/2026-04-30/harness-risk-gate-extension.md
20
+ // (design phase B).
21
+ import { RiskSeveritySchema } from "../schema/index.js";
22
+ import { expandToolNameAliases, extractShellCommand } from "./tool-name-aliases.js";
23
+ // Ordered severity scale: a value's index here is the comparison key
24
+ // for "highest matched severity wins". Sourced from the schema enum so
25
+ // a future reordering there flows through unchanged.
26
+ const SEVERITY_ORDER = RiskSeveritySchema.options;
27
+ // Categories that mean the action does not cleanly undo itself. When a
28
+ // matched pattern carries any of these the profile is `reversible:
29
+ // false`. `destructive` and `data_loss` are included alongside the
30
+ // explicit `irreversible_action`: a regex classifier cannot prove an
31
+ // action is safely undoable, and the Risk Gate exists to err toward
32
+ // caution. A genuinely destructive-but-reversible action simply should
33
+ // not be tagged `destructive` by its classifier author.
34
+ const IRREVERSIBLE_CATEGORIES = new Set(["irreversible_action", "data_loss", "destructive"]);
35
+ /**
36
+ * The string a classifier's patterns are regex-matched against. For a
37
+ * shell-class tool (or any tool whose input carries a `command` / `cmd`
38
+ * field) it is that command. For other tools it is the serialized raw
39
+ * input — blunt, but it keeps non-shell classifiers usable in the MVP.
40
+ */
41
+ function subjectFor(envelope) {
42
+ const command = extractShellCommand({ raw_input: envelope.raw_input });
43
+ if (command !== null)
44
+ return command;
45
+ const raw = envelope.raw_input;
46
+ if (raw === null || raw === undefined)
47
+ return "";
48
+ if (typeof raw === "string")
49
+ return raw;
50
+ try {
51
+ return JSON.stringify(raw);
52
+ }
53
+ catch {
54
+ return "";
55
+ }
56
+ }
57
+ /** A classifier applies when its `tool` is an alias of the envelope's tool. */
58
+ function classifierApplies(classifier, envelope) {
59
+ return expandToolNameAliases(envelope.tool).includes(classifier.tool);
60
+ }
61
+ /**
62
+ * Classify an Action Envelope against the manifest's risk classifiers.
63
+ *
64
+ * Pure: envelope + classifiers in, profile out, no I/O. Multiple
65
+ * matching patterns compose — highest severity wins, categories union,
66
+ * one `reasons` line per hit. An envelope no pattern matches yields the
67
+ * honest unclassified profile (`classified: false`, `severity: null`).
68
+ */
69
+ export function classifyRisk(envelope, classifiers) {
70
+ const applicable = classifiers.filter((c) => classifierApplies(c, envelope));
71
+ const subject = subjectFor(envelope);
72
+ const categories = new Set();
73
+ const reasons = [];
74
+ let severityIdx = -1;
75
+ for (const classifier of applicable) {
76
+ for (const pat of classifier.patterns) {
77
+ let re;
78
+ try {
79
+ re = new RegExp(pat.pattern);
80
+ }
81
+ catch {
82
+ // The schema regex-validates patterns at parse time; this guard
83
+ // only covers a manifest that bypassed `harness validate`.
84
+ continue;
85
+ }
86
+ if (!re.test(subject))
87
+ continue;
88
+ for (const cat of pat.categories)
89
+ categories.add(cat);
90
+ const idx = SEVERITY_ORDER.indexOf(pat.severity);
91
+ if (idx > severityIdx)
92
+ severityIdx = idx;
93
+ reasons.push(`classifier "${classifier.name}" pattern /${pat.pattern}/ matched: ` +
94
+ `severity ${pat.severity}, categories [${pat.categories.join(", ")}]`);
95
+ }
96
+ }
97
+ if (severityIdx === -1) {
98
+ return {
99
+ classified: false,
100
+ severity: null,
101
+ categories: [],
102
+ reversible: null,
103
+ confidence: "low",
104
+ reasons: [
105
+ applicable.length === 0
106
+ ? `no risk classifier is declared for tool "${envelope.tool}"`
107
+ : `no classifier pattern matched the action for tool "${envelope.tool}"`,
108
+ ],
109
+ };
110
+ }
111
+ const sortedCategories = [...categories].sort();
112
+ return {
113
+ classified: true,
114
+ severity: SEVERITY_ORDER[severityIdx],
115
+ categories: sortedCategories,
116
+ reversible: !sortedCategories.some((c) => IRREVERSIBLE_CATEGORIES.has(c)),
117
+ confidence: "high",
118
+ reasons,
119
+ };
120
+ }
121
+ //# sourceMappingURL=risk-classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk-classifier.js","sourceRoot":"","sources":["../../src/runtime/risk-classifier.ts"],"names":[],"mappings":"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"}