@guilz-dev/belay 0.7.0 → 0.8.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/README.md +19 -19
  2. package/dist/adapters/shared/gate-runtime.js +23 -5
  3. package/dist/bundle/claude-runtime.mjs +4909 -3504
  4. package/dist/bundle/codex-runtime.mjs +5050 -3645
  5. package/dist/bundle/cursor-runtime.mjs +5050 -3645
  6. package/dist/commands/doctor.js +7 -0
  7. package/dist/commands/dogfood.js +1 -1
  8. package/dist/commands/explain.js +2 -2
  9. package/dist/commands/harvest.d.ts +0 -1
  10. package/dist/commands/harvest.js +1 -4
  11. package/dist/commands/metrics.js +6 -0
  12. package/dist/commands/quality.js +1 -3
  13. package/dist/conformance/guarantee-table.js +133 -11
  14. package/dist/conformance/types.d.ts +1 -0
  15. package/dist/core/audit-metrics.d.ts +1 -0
  16. package/dist/core/audit-metrics.js +12 -4
  17. package/dist/core/audit-types.d.ts +3 -0
  18. package/dist/core/capability/index.d.ts +1 -1
  19. package/dist/core/capability/policy-engine.d.ts +31 -1
  20. package/dist/core/capability/policy-engine.js +186 -0
  21. package/dist/core/capability/request.d.ts +9 -0
  22. package/dist/core/capability/resolver.d.ts +0 -1
  23. package/dist/core/capability/resolver.js +0 -1
  24. package/dist/core/config.js +0 -2
  25. package/dist/core/effect-ir/audit.js +1 -0
  26. package/dist/core/effect-ir/build.js +5 -1
  27. package/dist/core/effect-ir/index.d.ts +6 -1
  28. package/dist/core/effect-ir/index.js +2 -0
  29. package/dist/core/effect-ir/normalize.js +9 -3
  30. package/dist/core/effect-ir/policy.d.ts +2 -1
  31. package/dist/core/effect-ir/policy.js +93 -6
  32. package/dist/core/effect-ir/shell-build.d.ts +43 -0
  33. package/dist/core/effect-ir/shell-build.js +77 -0
  34. package/dist/core/effect-ir/shell-lower.d.ts +14 -0
  35. package/dist/core/effect-ir/shell-lower.js +1509 -0
  36. package/dist/core/effect-ir/types.d.ts +9 -2
  37. package/dist/core/gate-engine.js +63 -23
  38. package/dist/core/git-resource-identity.d.ts +26 -0
  39. package/dist/core/git-resource-identity.js +284 -0
  40. package/dist/core/harvest.d.ts +3 -7
  41. package/dist/core/harvest.js +4 -27
  42. package/dist/core/index.d.ts +2 -0
  43. package/dist/core/index.js +1 -0
  44. package/dist/core/path-utils.js +42 -23
  45. package/dist/core/shell-substitution.d.ts +1 -0
  46. package/dist/core/shell-substitution.js +61 -0
  47. package/dist/core/standing-allow.d.ts +1 -2
  48. package/dist/core/standing-allow.js +3 -20
  49. package/dist/core/types.d.ts +4 -1
  50. package/dist/core/verdict/adapter.js +15 -15
  51. package/dist/core/verdict/containment.js +4 -0
  52. package/dist/core/verdict/egress-classify.d.ts +12 -0
  53. package/dist/core/verdict/egress-classify.js +746 -0
  54. package/dist/core/verdict/git-classifier.d.ts +12 -0
  55. package/dist/core/verdict/git-classifier.js +320 -0
  56. package/dist/core/verdict/launcher-resolve.js +77 -17
  57. package/dist/core/verdict/parser.d.ts +2 -0
  58. package/dist/core/verdict/parser.js +95 -1
  59. package/dist/core/verdict/types.d.ts +2 -3
  60. package/dist/core/verdict/verdict.js +103 -952
  61. package/dist/corpus/must-allow-commands.d.ts +2 -1
  62. package/dist/corpus/must-allow-commands.js +2 -1
  63. package/dist/corpus/runtime-match.d.ts +2 -1
  64. package/dist/corpus/runtime-match.js +2 -1
  65. package/dist/corpus/types.d.ts +2 -2
  66. package/dist/templates.js +7 -2
  67. package/dist/version.d.ts +1 -1
  68. package/dist/version.js +1 -1
  69. package/package.json +2 -2
  70. package/dist/core/verdict/overrides.d.ts +0 -7
  71. package/dist/core/verdict/overrides.js +0 -37
  72. package/dist/core/verdict/shell-policy.d.ts +0 -26
  73. package/dist/core/verdict/shell-policy.js +0 -40
  74. package/dist/corpus/standing-allow-catalog.generated.d.ts +0 -13
  75. package/dist/corpus/standing-allow-catalog.generated.js +0 -99
@@ -2,6 +2,10 @@ export declare const CAPABILITY_REQUEST_VERSION: 1;
2
2
  export type CapabilityAction = 'fs.read' | 'fs.write' | 'process.exec' | 'network.connect' | 'secret.read' | 'git.ref.write' | 'control_plane.write' | 'indeterminate';
3
3
  export type CapabilityEvidenceLevel = 'certain' | 'possible' | 'indeterminate';
4
4
  export type CapabilityHookKind = 'shell' | 'tool' | 'subagent';
5
+ export type NetworkMode = 'read' | 'mutate' | 'ambiguous';
6
+ export type NetworkPayload = 'none' | 'present' | 'secret';
7
+ export type ProcessOperation = 'inspect' | 'spawn' | 'signal';
8
+ export type GitRefScope = 'local' | 'remote';
5
9
  export interface CapabilityPrincipal {
6
10
  adapter?: string;
7
11
  repoRoot: string;
@@ -15,15 +19,20 @@ export type CapabilityResource = {
15
19
  host: string;
16
20
  port?: number;
17
21
  protocol?: string;
22
+ mode?: NetworkMode;
23
+ payload?: NetworkPayload;
18
24
  } | {
19
25
  kind: 'executable';
20
26
  command: string;
27
+ operation?: ProcessOperation;
21
28
  } | {
22
29
  kind: 'package-cache';
23
30
  manager: 'npm' | 'pnpm';
24
31
  } | {
25
32
  kind: 'git-ref';
26
33
  ref: string;
34
+ scope?: GitRefScope;
35
+ repoPath?: string;
27
36
  } | {
28
37
  kind: 'unknown';
29
38
  };
@@ -1,4 +1,3 @@
1
- export { evaluateSegmentShellPolicy, type SegmentShellPolicyInput, } from '../verdict/shell-policy.js';
2
1
  export { BOUNDARY_PROFILE_L3_POLICY, checkGatedActionLimits } from './limits.js';
3
2
  export { type CapabilityAuthorizationMetadata, policyReasonToLegacyReason, singleCapabilityMetadata, } from './policy-bridge.js';
4
3
  export { buildFileMutationCapabilityRequest, buildShellCapabilityRequest, buildSubagentCapabilityRequest, evaluateFileMutationPolicy, evaluateShellPolicy, evaluateSubagentPolicy, type FileMutationCapabilityAnalysis, policyDecisionRequiresAsk, type ShellCapabilityAnalysis, type SubagentCapabilityAnalysis, } from './policy-engine.js';
@@ -1,4 +1,3 @@
1
- export { evaluateSegmentShellPolicy, } from '../verdict/shell-policy.js';
2
1
  export { BOUNDARY_PROFILE_L3_POLICY, checkGatedActionLimits } from './limits.js';
3
2
  export { policyReasonToLegacyReason, singleCapabilityMetadata, } from './policy-bridge.js';
4
3
  export { buildFileMutationCapabilityRequest, buildShellCapabilityRequest, buildSubagentCapabilityRequest, evaluateFileMutationPolicy, evaluateShellPolicy, evaluateSubagentPolicy, policyDecisionRequiresAsk, } from './policy-engine.js';
@@ -937,8 +937,6 @@ export function scrubOptionsFromConfig(config) {
937
937
  export function classifierOptionsFromConfig(config) {
938
938
  return {
939
939
  strictChains: config.classifier.strictChains,
940
- customExternalCommands: config.overrides.external,
941
- customAllowCommands: config.overrides.allow,
942
940
  sensitivePaths: config.classifier.sensitivePaths,
943
941
  unknownLocalEffect: config.policy.unknownLocalEffect,
944
942
  unparseableShell: config.policy.unparseableShell,
@@ -12,6 +12,7 @@ function auditableResource(resource) {
12
12
  return {
13
13
  kind: 'executable',
14
14
  commandHash: hashValue(resource.command),
15
+ ...(resource.operation ? { operation: resource.operation } : {}),
15
16
  };
16
17
  }
17
18
  return resource;
@@ -72,7 +72,11 @@ function delegateExecNode(peel, innerRecipe, segmentHead, resolvedLocalPath) {
72
72
  {
73
73
  tag: 'process.exec',
74
74
  action: 'process.exec',
75
- resource: { kind: 'executable', command: resolvedLocalPath ?? segmentHead },
75
+ resource: {
76
+ kind: 'executable',
77
+ command: resolvedLocalPath ?? segmentHead,
78
+ operation: 'spawn',
79
+ },
76
80
  evidence: {
77
81
  level: resolvedLocalPath ? 'certain' : peel.forceAcquire ? 'possible' : 'certain',
78
82
  signals: [
@@ -1,8 +1,13 @@
1
+ export type { GitRefScope, NetworkMode, NetworkPayload, ProcessOperation, } from '../capability/request.js';
1
2
  export { effectPlanAuditFields, hashEffectPlan } from './audit.js';
2
3
  export { buildCapabilityEffectPlan, buildEffectPlan, buildPackageExecEffectNode, collectRequirements, flattenRequirementsToCapabilityRequests, } from './build.js';
3
4
  export { mergeEffectPlans, mergeRequirements, normalizeEffectTags } from './normalize.js';
4
5
  export { innerRecipeFromPeel, isPackageExecLauncher, peelPackageExecArgv, resolveLocalBin, } from './package-exec.js';
5
6
  export { evaluatePackageExecSegment } from './package-exec-eval.js';
6
7
  export { capabilityRequestsFromEffectPlan, capabilityRequestsToEffectRequirements, effectPlanPolicyLegacyReason, effectPlanPolicyRequiresAsk, evaluateEffectPlanPolicy, } from './policy.js';
7
- export type { EffectEvidence, EffectNode, EffectPlan, EffectProvenance, EffectRequirement, EffectTag, ExecEffectNode, LauncherEffectNode, LauncherPhase, MergeEffectNode, PackageExecLauncher, } from './types.js';
8
+ export type { BuildShellEffectPlanParams, ShellEffectRequirement, ShellEffectResource, ShellEffectSegment, } from './shell-build.js';
9
+ export { buildShellEffectPlan } from './shell-build.js';
10
+ export type { LowerShellEffectPlanParams } from './shell-lower.js';
11
+ export { lowerShellEffectPlan } from './shell-lower.js';
12
+ export type { AnalysisCompleteness, EffectEvidence, EffectNode, EffectPlan, EffectPlanDisposition, EffectPlanPolicyProjection, EffectProvenance, EffectRequirement, EffectTag, ExecEffectNode, LauncherEffectNode, LauncherPhase, MergeEffectNode, PackageExecLauncher, } from './types.js';
8
13
  export { EFFECT_PLAN_VERSION } from './types.js';
@@ -4,4 +4,6 @@ export { mergeEffectPlans, mergeRequirements, normalizeEffectTags } from './norm
4
4
  export { innerRecipeFromPeel, isPackageExecLauncher, peelPackageExecArgv, resolveLocalBin, } from './package-exec.js';
5
5
  export { evaluatePackageExecSegment } from './package-exec-eval.js';
6
6
  export { capabilityRequestsFromEffectPlan, capabilityRequestsToEffectRequirements, effectPlanPolicyLegacyReason, effectPlanPolicyRequiresAsk, evaluateEffectPlanPolicy, } from './policy.js';
7
+ export { buildShellEffectPlan } from './shell-build.js';
8
+ export { lowerShellEffectPlan } from './shell-lower.js';
7
9
  export { EFFECT_PLAN_VERSION } from './types.js';
@@ -77,13 +77,19 @@ function resourceKey(resource) {
77
77
  case 'path':
78
78
  return resource.path;
79
79
  case 'network':
80
- return `${resource.host}:${resource.port ?? ''}:${resource.protocol ?? ''}`;
80
+ return [
81
+ resource.host,
82
+ resource.port ?? '',
83
+ resource.protocol ?? '',
84
+ resource.mode ?? '',
85
+ resource.payload ?? '',
86
+ ].join(':');
81
87
  case 'executable':
82
- return resource.command;
88
+ return `${resource.command}:${resource.operation ?? ''}`;
83
89
  case 'package-cache':
84
90
  return resource.manager;
85
91
  case 'git-ref':
86
- return resource.ref;
92
+ return `${resource.ref}:${resource.scope ?? ''}:${resource.repoPath ?? ''}`;
87
93
  case 'unknown':
88
94
  return 'unknown';
89
95
  default:
@@ -1,13 +1,14 @@
1
1
  import type { PolicyDecision } from '../capability/policy-types.js';
2
2
  import type { CapabilityRequestV1 } from '../capability/request.js';
3
3
  import type { VerdictContext } from '../verdict/types.js';
4
- import type { EffectPlan, EffectRequirement } from './types.js';
4
+ import type { EffectPlan, EffectPlanPolicyProjection, EffectRequirement } from './types.js';
5
5
  export declare function capabilityRequestsToEffectRequirements(requests: readonly CapabilityRequestV1[]): EffectRequirement[];
6
6
  export declare function capabilityRequestsFromEffectPlan(plan: EffectPlan, context: VerdictContext): CapabilityRequestV1[];
7
7
  export declare function evaluateEffectPlanPolicy(plan: EffectPlan, context: VerdictContext): {
8
8
  capabilityRequests: CapabilityRequestV1[];
9
9
  decisions: PolicyDecision[];
10
10
  authorizationDecision: PolicyDecision;
11
+ projection: EffectPlanPolicyProjection;
11
12
  };
12
13
  export declare function effectPlanPolicyRequiresAsk(decision: PolicyDecision): boolean;
13
14
  export declare function effectPlanPolicyLegacyReason(decision: PolicyDecision): string;
@@ -1,6 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { policyDecisionToLegacyReason } from '../capability/policy-bridge.js';
3
- import { evaluateCapabilityRequestsPolicy, policyDecisionRequiresAsk, } from '../capability/policy-engine.js';
3
+ import { evaluateEffectRequirementPolicy, policyDecisionRequiresAsk, } from '../capability/policy-engine.js';
4
4
  import { collectRequirements, flattenRequirementsToCapabilityRequests } from './build.js';
5
5
  function shellPrincipal(repoRoot, cwd) {
6
6
  return {
@@ -61,13 +61,100 @@ export function capabilityRequestsFromEffectPlan(plan, context) {
61
61
  });
62
62
  }
63
63
  export function evaluateEffectPlanPolicy(plan, context) {
64
+ const requirements = collectRequirements(plan.root);
64
65
  const capabilityRequests = capabilityRequestsFromEffectPlan(plan, context);
65
- const { decisions, decision } = evaluateCapabilityRequestsPolicy(capabilityRequests, context.config, {
66
+ const decisions = requirements.map((requirement, index) => evaluateEffectRequirementPolicy({
67
+ tag: requirement.tag,
68
+ action: requirement.action,
69
+ resource: requirement.resource,
70
+ evidence: requirement.evidence,
71
+ provenance: requirement.provenance,
72
+ }, {
73
+ cwd: context.cwd,
74
+ repoRoot: context.repoRoot,
75
+ trustedWorkspaceRoots: context.trustedWorkspaceRoots,
76
+ sensitivePaths: context.sensitivePaths,
77
+ protectedArtifactRoots: context.protectedArtifactRoots,
66
78
  grants: context.grants,
67
- attestation: context.attestation,
68
- egressProxyActive: context.egressProxyActive,
69
- }, context.trustedWorkspaceRoots);
70
- return { capabilityRequests, decisions, authorizationDecision: decision };
79
+ capabilityRequest: capabilityRequests[index],
80
+ }));
81
+ const indeterminateIndexes = requirements.flatMap((requirement, index) => requirement.action === 'indeterminate' ||
82
+ requirement.tag === 'indeterminate' ||
83
+ requirement.evidence.level === 'indeterminate'
84
+ ? [index]
85
+ : []);
86
+ const unresolvedPartial = indeterminateIndexes.length === 0 ||
87
+ indeterminateIndexes.some((index) => decisions[index]?.matchedRule !== 'grant.exact');
88
+ if (plan.completeness === 'partial' && unresolvedPartial) {
89
+ decisions.push({
90
+ effectDisposition: 'ask',
91
+ outcome: 'require_approval',
92
+ reason: 'indeterminate_effect',
93
+ signals: [...plan.signals, 'effect_plan_partial'],
94
+ matchedRule: 'effect.plan_partial',
95
+ });
96
+ }
97
+ const worstDecision = worstEffectDecision(decisions);
98
+ const authorizationDecision = (worstDecision?.effectDisposition === 'ask'
99
+ ? worstDecision
100
+ : (decisions.find((decision) => decision.matchedRule === 'grant.exact') ?? worstDecision)) ??
101
+ {
102
+ outcome: 'allow',
103
+ reason: 'read_only',
104
+ signals: [],
105
+ matchedRule: 'effect.effect_free',
106
+ };
107
+ const projection = projectEffectPlanPolicy(decisions, authorizationDecision);
108
+ return {
109
+ capabilityRequests,
110
+ decisions,
111
+ authorizationDecision,
112
+ projection,
113
+ };
114
+ }
115
+ function dispositionRank(disposition) {
116
+ switch (disposition) {
117
+ case 'ask':
118
+ return 2;
119
+ case 'allow_flagged':
120
+ return 1;
121
+ case 'allow':
122
+ return 0;
123
+ default:
124
+ return 2;
125
+ }
126
+ }
127
+ function projectEffectPlanPolicy(decisions, authorizationDecision) {
128
+ const worst = worstEffectDecision(decisions);
129
+ if (!worst) {
130
+ return {
131
+ permission: 'allow',
132
+ hookVerdict: 'allow',
133
+ reason: 'read_only',
134
+ };
135
+ }
136
+ if (worst.effectDisposition === 'ask') {
137
+ return {
138
+ permission: 'ask',
139
+ hookVerdict: 'deny_pending_approval',
140
+ reason: policyDecisionToLegacyReason(authorizationDecision),
141
+ };
142
+ }
143
+ return {
144
+ permission: 'allow',
145
+ hookVerdict: worst.effectDisposition === 'allow_flagged' ? 'allow_flagged' : 'allow',
146
+ reason: policyDecisionToLegacyReason(worst),
147
+ };
148
+ }
149
+ function worstEffectDecision(decisions) {
150
+ let worst;
151
+ for (const decision of decisions) {
152
+ if (!worst ||
153
+ dispositionRank(decision.effectDisposition) > dispositionRank(worst.effectDisposition)) {
154
+ worst = decision;
155
+ }
156
+ }
157
+ return worst;
71
158
  }
72
159
  export function effectPlanPolicyRequiresAsk(decision) {
73
160
  return policyDecisionRequiresAsk(decision);
@@ -0,0 +1,43 @@
1
+ import type { CapabilityResource } from '../capability/request.js';
2
+ import type { AnalysisCompleteness, EffectPlan, EffectRequirement } from './types.js';
3
+ type ShellNetworkResource = Extract<CapabilityResource, {
4
+ kind: 'network';
5
+ }> & Required<Pick<Extract<CapabilityResource, {
6
+ kind: 'network';
7
+ }>, 'mode' | 'payload'>>;
8
+ type ShellExecutableResource = Extract<CapabilityResource, {
9
+ kind: 'executable';
10
+ }> & Required<Pick<Extract<CapabilityResource, {
11
+ kind: 'executable';
12
+ }>, 'operation'>>;
13
+ type ShellGitRefResource = Extract<CapabilityResource, {
14
+ kind: 'git-ref';
15
+ }> & Required<Pick<Extract<CapabilityResource, {
16
+ kind: 'git-ref';
17
+ }>, 'scope'>>;
18
+ export type ShellEffectResource = Exclude<CapabilityResource, {
19
+ kind: 'network' | 'executable' | 'git-ref';
20
+ }> | ShellNetworkResource | ShellExecutableResource | ShellGitRefResource;
21
+ export type ShellEffectRequirement = Omit<EffectRequirement, 'resource'> & {
22
+ resource: ShellEffectResource;
23
+ };
24
+ export interface ShellEffectSegment {
25
+ commandRedacted: string;
26
+ segmentHead: string;
27
+ requirements: readonly ShellEffectRequirement[];
28
+ completeness: AnalysisCompleteness;
29
+ opacity: EffectPlan['opacity'];
30
+ signals: readonly string[];
31
+ }
32
+ export interface BuildShellEffectPlanParams {
33
+ inputFingerprint: string;
34
+ segments: readonly ShellEffectSegment[];
35
+ signals?: readonly string[];
36
+ }
37
+ /**
38
+ * Build a canonical EffectPlan from shell segments that have already been
39
+ * semantically analyzed. This function only assembles and normalizes effects;
40
+ * command-specific lowering belongs to the segment producers.
41
+ */
42
+ export declare function buildShellEffectPlan(params: BuildShellEffectPlanParams): EffectPlan;
43
+ export {};
@@ -0,0 +1,77 @@
1
+ import { collectRequirements } from './build.js';
2
+ import { mergeRequirements } from './normalize.js';
3
+ /**
4
+ * Build a canonical EffectPlan from shell segments that have already been
5
+ * semantically analyzed. This function only assembles and normalizes effects;
6
+ * command-specific lowering belongs to the segment producers.
7
+ */
8
+ export function buildShellEffectPlan(params) {
9
+ const nodes = params.segments.map(buildSegmentNode);
10
+ const root = mergeNodes(nodes);
11
+ const requirements = collectRequirements(root);
12
+ const completeness = requirements.some(isIndeterminateRequirement)
13
+ ? 'partial'
14
+ : 'complete';
15
+ return {
16
+ version: 1,
17
+ root,
18
+ inputFingerprint: params.inputFingerprint,
19
+ opacity: mergeOpacity(params.segments.map((segment) => segment.opacity)),
20
+ disposition: requirements.length === 0 ? 'effect_free' : 'effects',
21
+ completeness,
22
+ signals: [
23
+ ...new Set([
24
+ ...(params.signals ?? []),
25
+ ...params.segments.flatMap((segment) => [...segment.signals]),
26
+ ...requirements.flatMap((requirement) => [...requirement.evidence.signals]),
27
+ ]),
28
+ ].sort(),
29
+ };
30
+ }
31
+ function buildSegmentNode(segment) {
32
+ const requirements = mergeRequirements(segment.requirements);
33
+ const partial = segment.completeness === 'partial' ||
34
+ requirements.some((requirement) => requirement.evidence.level === 'indeterminate');
35
+ if (partial && !requirements.some(isIndeterminateRequirement)) {
36
+ const provenance = { segment: segment.commandRedacted };
37
+ requirements.push({
38
+ tag: 'indeterminate',
39
+ action: 'indeterminate',
40
+ resource: { kind: 'unknown' },
41
+ evidence: {
42
+ level: 'indeterminate',
43
+ signals: [...new Set([...segment.signals, 'effect_plan.segment_partial'])],
44
+ basis: [`shell_segment:${segment.segmentHead}`],
45
+ },
46
+ provenance,
47
+ provenances: [provenance],
48
+ });
49
+ }
50
+ return {
51
+ kind: 'exec',
52
+ commandRedacted: segment.commandRedacted,
53
+ segmentHead: segment.segmentHead,
54
+ requirements,
55
+ };
56
+ }
57
+ function isIndeterminateRequirement(requirement) {
58
+ return requirement.tag === 'indeterminate' || requirement.action === 'indeterminate';
59
+ }
60
+ function mergeNodes(nodes) {
61
+ if (nodes.length === 1 && nodes[0]) {
62
+ return nodes[0];
63
+ }
64
+ return { kind: 'merge', children: nodes };
65
+ }
66
+ function mergeOpacity(opacities) {
67
+ if (opacities.includes('unparseable')) {
68
+ return 'unparseable';
69
+ }
70
+ if (opacities.includes('opaque')) {
71
+ return 'opaque';
72
+ }
73
+ if (opacities.includes('recursive')) {
74
+ return 'recursive';
75
+ }
76
+ return 'transparent';
77
+ }
@@ -0,0 +1,14 @@
1
+ import type { EffectPlan } from './types.js';
2
+ export interface LowerShellEffectPlanParams {
3
+ command: string;
4
+ cwd: string;
5
+ repoRoot: string;
6
+ inputFingerprint: string;
7
+ env?: Readonly<Record<string, string | undefined>>;
8
+ }
9
+ /**
10
+ * Parse and lower a general shell command into the canonical Task 2 model.
11
+ * This function only produces effects; it does not evaluate policy or return a
12
+ * permission/verdict.
13
+ */
14
+ export declare function lowerShellEffectPlan(params: LowerShellEffectPlanParams): EffectPlan;