@caupulican/pi-adaptative 0.80.29 → 0.80.31
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 +13 -0
- package/dist/core/agent-session.d.ts +3 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +4 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/context-gc.d.ts.map +1 -1
- package/dist/core/context-gc.js +93 -60
- package/dist/core/context-gc.js.map +1 -1
- package/dist/core/improvement-loop.d.ts +296 -0
- package/dist/core/improvement-loop.d.ts.map +1 -0
- package/dist/core/improvement-loop.js +906 -0
- package/dist/core/improvement-loop.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +5 -0
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { Type } from "typebox";
|
|
2
|
+
import { type ToolDefinition } from "./extensions/types.ts";
|
|
3
|
+
export type MetricDirection = "lower" | "higher";
|
|
4
|
+
export type ImprovementDecision = "keep" | "discard" | "retry" | "blocked";
|
|
5
|
+
export type ImprovementDecisionReason = "baseline" | "metric_improved" | "checks_failed" | "metric_missing" | "metric_invalid" | "not_better_than_best" | "below_min_delta" | "below_min_relative_delta" | "insufficient_noise_evidence" | "below_confidence";
|
|
6
|
+
export type ConfidenceMode = "none" | "mad";
|
|
7
|
+
export type LowConfidenceAction = "retry" | "discard";
|
|
8
|
+
export interface MetricComparison {
|
|
9
|
+
current: number;
|
|
10
|
+
best: number | null;
|
|
11
|
+
direction: MetricDirection;
|
|
12
|
+
delta: number;
|
|
13
|
+
relativeDelta: number | null;
|
|
14
|
+
improved: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface ConfidenceResult {
|
|
17
|
+
mode: ConfidenceMode;
|
|
18
|
+
value: number | null;
|
|
19
|
+
noiseFloor: number | null;
|
|
20
|
+
sampleCount: number;
|
|
21
|
+
}
|
|
22
|
+
export interface ImprovementDecisionInput {
|
|
23
|
+
currentMetric: number | null | undefined;
|
|
24
|
+
bestMetric?: number | null;
|
|
25
|
+
baselineMetric?: number | null;
|
|
26
|
+
direction?: MetricDirection;
|
|
27
|
+
checksPass?: boolean | null;
|
|
28
|
+
minDelta?: number;
|
|
29
|
+
minRelativeDelta?: number;
|
|
30
|
+
confidenceMode?: ConfidenceMode;
|
|
31
|
+
minConfidence?: number;
|
|
32
|
+
lowConfidenceAction?: LowConfidenceAction;
|
|
33
|
+
noiseMetrics?: number[];
|
|
34
|
+
}
|
|
35
|
+
export interface ImprovementDecisionResult {
|
|
36
|
+
decision: ImprovementDecision;
|
|
37
|
+
reason: ImprovementDecisionReason;
|
|
38
|
+
comparison: MetricComparison | null;
|
|
39
|
+
confidence: ConfidenceResult;
|
|
40
|
+
}
|
|
41
|
+
export interface ImprovementRunRecord {
|
|
42
|
+
runId: string | number;
|
|
43
|
+
objective?: string;
|
|
44
|
+
hypothesis?: string;
|
|
45
|
+
metricName: string;
|
|
46
|
+
metricUnit?: string;
|
|
47
|
+
direction: MetricDirection;
|
|
48
|
+
metric: number | null;
|
|
49
|
+
secondaryMetrics?: Record<string, number>;
|
|
50
|
+
checksPass: boolean | null;
|
|
51
|
+
decision: ImprovementDecision;
|
|
52
|
+
reason: ImprovementDecisionReason;
|
|
53
|
+
confidence?: number | null;
|
|
54
|
+
changedFiles?: string[];
|
|
55
|
+
evidenceRef?: string;
|
|
56
|
+
nextHint?: string;
|
|
57
|
+
timestamp: number;
|
|
58
|
+
}
|
|
59
|
+
export interface GitStatusEntry {
|
|
60
|
+
index: string;
|
|
61
|
+
workingTree: string;
|
|
62
|
+
path: string;
|
|
63
|
+
origPath?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface OwnedDiscardPlanInput {
|
|
66
|
+
beforeStatus: string | GitStatusEntry[];
|
|
67
|
+
afterStatus: string | GitStatusEntry[];
|
|
68
|
+
ownedPaths: string[];
|
|
69
|
+
preservePaths?: string[];
|
|
70
|
+
}
|
|
71
|
+
export interface OwnedDiscardPlan {
|
|
72
|
+
revertPaths: string[];
|
|
73
|
+
preservePaths: string[];
|
|
74
|
+
protectedUserDirtyPaths: string[];
|
|
75
|
+
unownedChangedPaths: string[];
|
|
76
|
+
canDiscardOwnedChanges: boolean;
|
|
77
|
+
}
|
|
78
|
+
export interface ImprovementLoopConfig {
|
|
79
|
+
loopId: string;
|
|
80
|
+
objective: string;
|
|
81
|
+
metricName: string;
|
|
82
|
+
metricUnit?: string;
|
|
83
|
+
direction: MetricDirection;
|
|
84
|
+
minDelta: number;
|
|
85
|
+
minRelativeDelta: number;
|
|
86
|
+
confidenceMode: ConfidenceMode;
|
|
87
|
+
minConfidence: number;
|
|
88
|
+
lowConfidenceAction: LowConfidenceAction;
|
|
89
|
+
createdAt: number;
|
|
90
|
+
cwd: string;
|
|
91
|
+
}
|
|
92
|
+
export interface ImprovementSandboxRecord {
|
|
93
|
+
sandboxId: string;
|
|
94
|
+
status: "active" | "cleaned";
|
|
95
|
+
repoPath: string;
|
|
96
|
+
worktreePath: string;
|
|
97
|
+
baseRef: string;
|
|
98
|
+
createdAt: number;
|
|
99
|
+
cleanedAt?: number;
|
|
100
|
+
exportedAt?: number;
|
|
101
|
+
patchPath?: string;
|
|
102
|
+
patchBytes?: number;
|
|
103
|
+
reason?: string;
|
|
104
|
+
}
|
|
105
|
+
export interface ImprovementLoopState {
|
|
106
|
+
config: ImprovementLoopConfig;
|
|
107
|
+
runs: ImprovementRunRecord[];
|
|
108
|
+
sandboxes: ImprovementSandboxRecord[];
|
|
109
|
+
activeSandbox: ImprovementSandboxRecord | null;
|
|
110
|
+
baselineMetric: number | null;
|
|
111
|
+
bestMetric: number | null;
|
|
112
|
+
bestRunId: string | number | null;
|
|
113
|
+
lastDecision: ImprovementDecisionResult | null;
|
|
114
|
+
logPath: string;
|
|
115
|
+
}
|
|
116
|
+
export interface ImprovementLoopPaths {
|
|
117
|
+
rootDir: string;
|
|
118
|
+
workspaceDir: string;
|
|
119
|
+
logPath: string;
|
|
120
|
+
sandboxDir: string;
|
|
121
|
+
artifactDir: string;
|
|
122
|
+
workspaceKey: string;
|
|
123
|
+
loopId: string;
|
|
124
|
+
}
|
|
125
|
+
export interface ImprovementLoopInitInput {
|
|
126
|
+
cwd: string;
|
|
127
|
+
objective: string;
|
|
128
|
+
metricName: string;
|
|
129
|
+
metricUnit?: string;
|
|
130
|
+
direction?: MetricDirection;
|
|
131
|
+
loopId?: string;
|
|
132
|
+
minDelta?: number;
|
|
133
|
+
minRelativeDelta?: number;
|
|
134
|
+
confidenceMode?: ConfidenceMode;
|
|
135
|
+
minConfidence?: number;
|
|
136
|
+
lowConfidenceAction?: LowConfidenceAction;
|
|
137
|
+
agentDir?: string;
|
|
138
|
+
reset?: boolean;
|
|
139
|
+
}
|
|
140
|
+
export interface ImprovementLoopRecordInput {
|
|
141
|
+
cwd: string;
|
|
142
|
+
loopId?: string;
|
|
143
|
+
runId?: string | number;
|
|
144
|
+
metric: number | null | undefined;
|
|
145
|
+
secondaryMetrics?: Record<string, number>;
|
|
146
|
+
checksPass?: boolean | null;
|
|
147
|
+
hypothesis?: string;
|
|
148
|
+
changedFiles?: string[];
|
|
149
|
+
evidenceRef?: string;
|
|
150
|
+
nextHint?: string;
|
|
151
|
+
agentDir?: string;
|
|
152
|
+
}
|
|
153
|
+
export interface ImprovementLoopStatusInput {
|
|
154
|
+
cwd: string;
|
|
155
|
+
loopId?: string;
|
|
156
|
+
agentDir?: string;
|
|
157
|
+
}
|
|
158
|
+
export interface ImprovementSandboxCreateInput extends ImprovementLoopStatusInput {
|
|
159
|
+
exec: ImprovementLoopExec;
|
|
160
|
+
baseRef?: string;
|
|
161
|
+
allowDirtyRepo?: boolean;
|
|
162
|
+
sandboxId?: string;
|
|
163
|
+
signal?: AbortSignal;
|
|
164
|
+
}
|
|
165
|
+
export interface ImprovementSandboxCleanupInput extends ImprovementLoopStatusInput {
|
|
166
|
+
exec: ImprovementLoopExec;
|
|
167
|
+
sandboxId?: string;
|
|
168
|
+
reason?: string;
|
|
169
|
+
signal?: AbortSignal;
|
|
170
|
+
}
|
|
171
|
+
export interface ImprovementSandboxExportInput extends ImprovementLoopStatusInput {
|
|
172
|
+
exec: ImprovementLoopExec;
|
|
173
|
+
sandboxId?: string;
|
|
174
|
+
allowEmptyPatch?: boolean;
|
|
175
|
+
signal?: AbortSignal;
|
|
176
|
+
}
|
|
177
|
+
export type ImprovementLoopToolAction = "init" | "status" | "record" | "measure" | "sandbox_create" | "sandbox_export" | "sandbox_cleanup";
|
|
178
|
+
export interface ImprovementMeasurementCommandResult {
|
|
179
|
+
stdout: string;
|
|
180
|
+
stderr: string;
|
|
181
|
+
code: number;
|
|
182
|
+
killed: boolean;
|
|
183
|
+
stdoutTruncated?: boolean;
|
|
184
|
+
stderrTruncated?: boolean;
|
|
185
|
+
}
|
|
186
|
+
export interface ImprovementMeasurementResult {
|
|
187
|
+
command: string;
|
|
188
|
+
exitCode: number;
|
|
189
|
+
timedOut: boolean;
|
|
190
|
+
durationMs: number;
|
|
191
|
+
stdout: string;
|
|
192
|
+
stderr: string;
|
|
193
|
+
stdoutTruncated: boolean;
|
|
194
|
+
stderrTruncated: boolean;
|
|
195
|
+
parsedMetrics: Record<string, number>;
|
|
196
|
+
primaryMetric: number | null;
|
|
197
|
+
checksCommand?: string;
|
|
198
|
+
checksExitCode?: number;
|
|
199
|
+
checksTimedOut?: boolean;
|
|
200
|
+
checksPass: boolean;
|
|
201
|
+
checksStdout?: string;
|
|
202
|
+
checksStderr?: string;
|
|
203
|
+
}
|
|
204
|
+
export type ImprovementLoopExec = (command: string, args: string[], options?: {
|
|
205
|
+
cwd?: string;
|
|
206
|
+
timeout?: number;
|
|
207
|
+
signal?: AbortSignal;
|
|
208
|
+
maxBuffer?: number;
|
|
209
|
+
}) => Promise<ImprovementMeasurementCommandResult>;
|
|
210
|
+
export interface ImprovementLoopToolDetails {
|
|
211
|
+
action: ImprovementLoopToolAction;
|
|
212
|
+
state: ImprovementLoopState | null;
|
|
213
|
+
decision?: ImprovementDecisionResult;
|
|
214
|
+
measurement?: ImprovementMeasurementResult;
|
|
215
|
+
sandbox?: ImprovementSandboxRecord;
|
|
216
|
+
logPath: string;
|
|
217
|
+
}
|
|
218
|
+
export declare function parseMetricLines(output: string): Map<string, number>;
|
|
219
|
+
export declare function metricMapFromOutput(output: string): Record<string, number>;
|
|
220
|
+
export declare function selectPrimaryMetric(metrics: Map<string, number> | Record<string, number>, metricName: string): number | null;
|
|
221
|
+
export declare function compareMetric(current: number, best: number | null | undefined, direction?: MetricDirection): MetricComparison;
|
|
222
|
+
export declare function median(values: number[]): number | null;
|
|
223
|
+
export declare function medianAbsoluteDeviation(values: number[]): number | null;
|
|
224
|
+
export declare function computeMadConfidence(improvementDelta: number, samples: number[]): ConfidenceResult;
|
|
225
|
+
export declare function decideImprovement(input: ImprovementDecisionInput): ImprovementDecisionResult;
|
|
226
|
+
export declare function serializeRunRecord(record: ImprovementRunRecord): string;
|
|
227
|
+
export declare function appendRunRecord(filePath: string, record: ImprovementRunRecord): Promise<void>;
|
|
228
|
+
export declare function readRunRecords(filePath: string): Promise<ImprovementRunRecord[]>;
|
|
229
|
+
export declare function improvementLoopPaths(input: ImprovementLoopStatusInput): ImprovementLoopPaths;
|
|
230
|
+
export declare function initImprovementLoop(input: ImprovementLoopInitInput): Promise<ImprovementLoopState>;
|
|
231
|
+
export declare function readImprovementLoopState(input: ImprovementLoopStatusInput): Promise<ImprovementLoopState | null>;
|
|
232
|
+
export declare function createImprovementSandbox(input: ImprovementSandboxCreateInput): Promise<ImprovementLoopState>;
|
|
233
|
+
export declare function exportImprovementSandboxPatch(input: ImprovementSandboxExportInput): Promise<ImprovementLoopState>;
|
|
234
|
+
export declare function cleanupImprovementSandbox(input: ImprovementSandboxCleanupInput): Promise<ImprovementLoopState>;
|
|
235
|
+
export declare function recordImprovementRun(input: ImprovementLoopRecordInput): Promise<ImprovementLoopState>;
|
|
236
|
+
export declare function runImprovementMeasurement(input: {
|
|
237
|
+
exec: ImprovementLoopExec;
|
|
238
|
+
cwd: string;
|
|
239
|
+
command: string;
|
|
240
|
+
metricName: string;
|
|
241
|
+
checksCommand?: string;
|
|
242
|
+
timeoutSeconds?: number;
|
|
243
|
+
checksTimeoutSeconds?: number;
|
|
244
|
+
maxOutputBytes?: number;
|
|
245
|
+
signal?: AbortSignal;
|
|
246
|
+
}): Promise<ImprovementMeasurementResult>;
|
|
247
|
+
export declare function parseGitPorcelainStatus(status: string): GitStatusEntry[];
|
|
248
|
+
export declare function planOwnedDiscard(input: OwnedDiscardPlanInput): OwnedDiscardPlan;
|
|
249
|
+
declare const DecisionToolParams: Type.TObject<{
|
|
250
|
+
currentMetric: Type.TNumber;
|
|
251
|
+
bestMetric: Type.TOptional<Type.TNumber>;
|
|
252
|
+
direction: Type.TUnsafe<"higher" | "lower">;
|
|
253
|
+
checksPass: Type.TOptional<Type.TBoolean>;
|
|
254
|
+
minDelta: Type.TOptional<Type.TNumber>;
|
|
255
|
+
minRelativeDelta: Type.TOptional<Type.TNumber>;
|
|
256
|
+
confidenceMode: Type.TOptional<Type.TUnsafe<"mad" | "none">>;
|
|
257
|
+
minConfidence: Type.TOptional<Type.TNumber>;
|
|
258
|
+
lowConfidenceAction: Type.TOptional<Type.TUnsafe<"discard" | "retry">>;
|
|
259
|
+
noiseMetrics: Type.TOptional<Type.TArray<Type.TNumber>>;
|
|
260
|
+
}>;
|
|
261
|
+
export declare function createImprovementDecisionTool(): ToolDefinition<typeof DecisionToolParams, ImprovementDecisionResult>;
|
|
262
|
+
declare const LoopToolParams: Type.TObject<{
|
|
263
|
+
action: Type.TUnsafe<"init" | "measure" | "record" | "sandbox_cleanup" | "sandbox_create" | "sandbox_export" | "status">;
|
|
264
|
+
loopId: Type.TOptional<Type.TString>;
|
|
265
|
+
objective: Type.TOptional<Type.TString>;
|
|
266
|
+
metricName: Type.TOptional<Type.TString>;
|
|
267
|
+
metricUnit: Type.TOptional<Type.TString>;
|
|
268
|
+
direction: Type.TOptional<Type.TUnsafe<"higher" | "lower">>;
|
|
269
|
+
reset: Type.TOptional<Type.TBoolean>;
|
|
270
|
+
minDelta: Type.TOptional<Type.TNumber>;
|
|
271
|
+
minRelativeDelta: Type.TOptional<Type.TNumber>;
|
|
272
|
+
confidenceMode: Type.TOptional<Type.TUnsafe<"mad" | "none">>;
|
|
273
|
+
minConfidence: Type.TOptional<Type.TNumber>;
|
|
274
|
+
lowConfidenceAction: Type.TOptional<Type.TUnsafe<"discard" | "retry">>;
|
|
275
|
+
currentMetric: Type.TOptional<Type.TNumber>;
|
|
276
|
+
checksPass: Type.TOptional<Type.TBoolean>;
|
|
277
|
+
hypothesis: Type.TOptional<Type.TString>;
|
|
278
|
+
secondaryMetrics: Type.TOptional<Type.TObject<{}>>;
|
|
279
|
+
changedFiles: Type.TOptional<Type.TArray<Type.TString>>;
|
|
280
|
+
evidenceRef: Type.TOptional<Type.TString>;
|
|
281
|
+
nextHint: Type.TOptional<Type.TString>;
|
|
282
|
+
command: Type.TOptional<Type.TString>;
|
|
283
|
+
checksCommand: Type.TOptional<Type.TString>;
|
|
284
|
+
timeoutSeconds: Type.TOptional<Type.TNumber>;
|
|
285
|
+
checksTimeoutSeconds: Type.TOptional<Type.TNumber>;
|
|
286
|
+
maxOutputBytes: Type.TOptional<Type.TNumber>;
|
|
287
|
+
useSandbox: Type.TOptional<Type.TBoolean>;
|
|
288
|
+
sandboxId: Type.TOptional<Type.TString>;
|
|
289
|
+
baseRef: Type.TOptional<Type.TString>;
|
|
290
|
+
allowDirtyRepo: Type.TOptional<Type.TBoolean>;
|
|
291
|
+
cleanupReason: Type.TOptional<Type.TString>;
|
|
292
|
+
allowEmptyPatch: Type.TOptional<Type.TBoolean>;
|
|
293
|
+
}>;
|
|
294
|
+
export declare function createImprovementLoopTool(exec?: ImprovementLoopExec): ToolDefinition<typeof LoopToolParams, ImprovementLoopToolDetails>;
|
|
295
|
+
export {};
|
|
296
|
+
//# sourceMappingURL=improvement-loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"improvement-loop.d.ts","sourceRoot":"","sources":["../../src/core/improvement-loop.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { StringEnum } from \"@caupulican/pi-ai\";\nimport { Type } from \"typebox\";\nimport { getAgentDir } from \"../config.ts\";\nimport { defineTool, type ToolDefinition } from \"./extensions/types.ts\";\n\nexport type MetricDirection = \"lower\" | \"higher\";\nexport type ImprovementDecision = \"keep\" | \"discard\" | \"retry\" | \"blocked\";\nexport type ImprovementDecisionReason =\n\t| \"baseline\"\n\t| \"metric_improved\"\n\t| \"checks_failed\"\n\t| \"metric_missing\"\n\t| \"metric_invalid\"\n\t| \"not_better_than_best\"\n\t| \"below_min_delta\"\n\t| \"below_min_relative_delta\"\n\t| \"insufficient_noise_evidence\"\n\t| \"below_confidence\";\nexport type ConfidenceMode = \"none\" | \"mad\";\nexport type LowConfidenceAction = \"retry\" | \"discard\";\n\nexport interface MetricComparison {\n\tcurrent: number;\n\tbest: number | null;\n\tdirection: MetricDirection;\n\tdelta: number;\n\trelativeDelta: number | null;\n\timproved: boolean;\n}\n\nexport interface ConfidenceResult {\n\tmode: ConfidenceMode;\n\tvalue: number | null;\n\tnoiseFloor: number | null;\n\tsampleCount: number;\n}\n\nexport interface ImprovementDecisionInput {\n\tcurrentMetric: number | null | undefined;\n\tbestMetric?: number | null;\n\tbaselineMetric?: number | null;\n\tdirection?: MetricDirection;\n\tchecksPass?: boolean | null;\n\tminDelta?: number;\n\tminRelativeDelta?: number;\n\tconfidenceMode?: ConfidenceMode;\n\tminConfidence?: number;\n\tlowConfidenceAction?: LowConfidenceAction;\n\tnoiseMetrics?: number[];\n}\n\nexport interface ImprovementDecisionResult {\n\tdecision: ImprovementDecision;\n\treason: ImprovementDecisionReason;\n\tcomparison: MetricComparison | null;\n\tconfidence: ConfidenceResult;\n}\n\nexport interface ImprovementRunRecord {\n\trunId: string | number;\n\tobjective?: string;\n\thypothesis?: string;\n\tmetricName: string;\n\tmetricUnit?: string;\n\tdirection: MetricDirection;\n\tmetric: number | null;\n\tsecondaryMetrics?: Record<string, number>;\n\tchecksPass: boolean | null;\n\tdecision: ImprovementDecision;\n\treason: ImprovementDecisionReason;\n\tconfidence?: number | null;\n\tchangedFiles?: string[];\n\tevidenceRef?: string;\n\tnextHint?: string;\n\ttimestamp: number;\n}\n\nexport interface GitStatusEntry {\n\tindex: string;\n\tworkingTree: string;\n\tpath: string;\n\torigPath?: string;\n}\n\nexport interface OwnedDiscardPlanInput {\n\tbeforeStatus: string | GitStatusEntry[];\n\tafterStatus: string | GitStatusEntry[];\n\townedPaths: string[];\n\tpreservePaths?: string[];\n}\n\nexport interface OwnedDiscardPlan {\n\trevertPaths: string[];\n\tpreservePaths: string[];\n\tprotectedUserDirtyPaths: string[];\n\tunownedChangedPaths: string[];\n\tcanDiscardOwnedChanges: boolean;\n}\n\nexport interface ImprovementLoopConfig {\n\tloopId: string;\n\tobjective: string;\n\tmetricName: string;\n\tmetricUnit?: string;\n\tdirection: MetricDirection;\n\tminDelta: number;\n\tminRelativeDelta: number;\n\tconfidenceMode: ConfidenceMode;\n\tminConfidence: number;\n\tlowConfidenceAction: LowConfidenceAction;\n\tcreatedAt: number;\n\tcwd: string;\n}\n\nexport interface ImprovementSandboxRecord {\n\tsandboxId: string;\n\tstatus: \"active\" | \"cleaned\";\n\trepoPath: string;\n\tworktreePath: string;\n\tbaseRef: string;\n\tcreatedAt: number;\n\tcleanedAt?: number;\n\texportedAt?: number;\n\tpatchPath?: string;\n\tpatchBytes?: number;\n\treason?: string;\n}\n\nexport interface ImprovementLoopState {\n\tconfig: ImprovementLoopConfig;\n\truns: ImprovementRunRecord[];\n\tsandboxes: ImprovementSandboxRecord[];\n\tactiveSandbox: ImprovementSandboxRecord | null;\n\tbaselineMetric: number | null;\n\tbestMetric: number | null;\n\tbestRunId: string | number | null;\n\tlastDecision: ImprovementDecisionResult | null;\n\tlogPath: string;\n}\n\nexport interface ImprovementLoopPaths {\n\trootDir: string;\n\tworkspaceDir: string;\n\tlogPath: string;\n\tsandboxDir: string;\n\tartifactDir: string;\n\tworkspaceKey: string;\n\tloopId: string;\n}\n\nexport interface ImprovementLoopInitInput {\n\tcwd: string;\n\tobjective: string;\n\tmetricName: string;\n\tmetricUnit?: string;\n\tdirection?: MetricDirection;\n\tloopId?: string;\n\tminDelta?: number;\n\tminRelativeDelta?: number;\n\tconfidenceMode?: ConfidenceMode;\n\tminConfidence?: number;\n\tlowConfidenceAction?: LowConfidenceAction;\n\tagentDir?: string;\n\treset?: boolean;\n}\n\nexport interface ImprovementLoopRecordInput {\n\tcwd: string;\n\tloopId?: string;\n\trunId?: string | number;\n\tmetric: number | null | undefined;\n\tsecondaryMetrics?: Record<string, number>;\n\tchecksPass?: boolean | null;\n\thypothesis?: string;\n\tchangedFiles?: string[];\n\tevidenceRef?: string;\n\tnextHint?: string;\n\tagentDir?: string;\n}\n\nexport interface ImprovementLoopStatusInput {\n\tcwd: string;\n\tloopId?: string;\n\tagentDir?: string;\n}\n\nexport interface ImprovementSandboxCreateInput extends ImprovementLoopStatusInput {\n\texec: ImprovementLoopExec;\n\tbaseRef?: string;\n\tallowDirtyRepo?: boolean;\n\tsandboxId?: string;\n\tsignal?: AbortSignal;\n}\n\nexport interface ImprovementSandboxCleanupInput extends ImprovementLoopStatusInput {\n\texec: ImprovementLoopExec;\n\tsandboxId?: string;\n\treason?: string;\n\tsignal?: AbortSignal;\n}\n\nexport interface ImprovementSandboxExportInput extends ImprovementLoopStatusInput {\n\texec: ImprovementLoopExec;\n\tsandboxId?: string;\n\tallowEmptyPatch?: boolean;\n\tsignal?: AbortSignal;\n}\n\ntype ImprovementLoopLogEntry =\n\t| ({ type: \"config\" } & ImprovementLoopConfig)\n\t| ({ type: \"run\" } & ImprovementRunRecord)\n\t| ({ type: \"sandbox\" } & ImprovementSandboxRecord);\n\nexport type ImprovementLoopToolAction =\n\t| \"init\"\n\t| \"status\"\n\t| \"record\"\n\t| \"measure\"\n\t| \"sandbox_create\"\n\t| \"sandbox_export\"\n\t| \"sandbox_cleanup\";\n\nexport interface ImprovementMeasurementCommandResult {\n\tstdout: string;\n\tstderr: string;\n\tcode: number;\n\tkilled: boolean;\n\tstdoutTruncated?: boolean;\n\tstderrTruncated?: boolean;\n}\n\nexport interface ImprovementMeasurementResult {\n\tcommand: string;\n\texitCode: number;\n\ttimedOut: boolean;\n\tdurationMs: number;\n\tstdout: string;\n\tstderr: string;\n\tstdoutTruncated: boolean;\n\tstderrTruncated: boolean;\n\tparsedMetrics: Record<string, number>;\n\tprimaryMetric: number | null;\n\tchecksCommand?: string;\n\tchecksExitCode?: number;\n\tchecksTimedOut?: boolean;\n\tchecksPass: boolean;\n\tchecksStdout?: string;\n\tchecksStderr?: string;\n}\n\nexport type ImprovementLoopExec = (\n\tcommand: string,\n\targs: string[],\n\toptions?: { cwd?: string; timeout?: number; signal?: AbortSignal; maxBuffer?: number },\n) => Promise<ImprovementMeasurementCommandResult>;\n\nexport interface ImprovementLoopToolDetails {\n\taction: ImprovementLoopToolAction;\n\tstate: ImprovementLoopState | null;\n\tdecision?: ImprovementDecisionResult;\n\tmeasurement?: ImprovementMeasurementResult;\n\tsandbox?: ImprovementSandboxRecord;\n\tlogPath: string;\n}\n\nconst METRIC_LINE_PREFIX = \"METRIC\";\nconst DENIED_METRIC_NAMES = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\nconst METRIC_NAME_RE = /^[\\w.µ]+$/u;\nconst DECIMAL_NUMBER_RE = /^[+-]?(?:(?:\\d+(?:\\.\\d*)?)|(?:\\.\\d+))(?:[eE][+-]?\\d+)?$/;\n\nfunction normalizeFiniteNumber(value: unknown): number | null {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) return null;\n\treturn value;\n}\n\nfunction normalizeNonNegative(value: number | undefined, fallback: number): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) return fallback;\n\treturn value;\n}\n\nfunction normalizeDirection(direction: MetricDirection | undefined): MetricDirection {\n\treturn direction === \"higher\" ? \"higher\" : \"lower\";\n}\n\nexport function parseMetricLines(output: string): Map<string, number> {\n\tconst metrics = new Map<string, number>();\n\tfor (const line of output.split(/\\r?\\n/)) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed.startsWith(`${METRIC_LINE_PREFIX} `)) continue;\n\t\tconst body = trimmed.slice(METRIC_LINE_PREFIX.length).trim();\n\t\tconst equals = body.indexOf(\"=\");\n\t\tif (equals <= 0) continue;\n\t\tconst name = body.slice(0, equals);\n\t\tconst rawValue = body.slice(equals + 1).trim();\n\t\tif (!METRIC_NAME_RE.test(name) || DENIED_METRIC_NAMES.has(name)) continue;\n\t\tif (!DECIMAL_NUMBER_RE.test(rawValue)) continue;\n\t\tconst value = Number(rawValue);\n\t\tif (Number.isFinite(value)) metrics.set(name, value);\n\t}\n\treturn metrics;\n}\n\nexport function metricMapFromOutput(output: string): Record<string, number> {\n\treturn Object.fromEntries(parseMetricLines(output));\n}\n\nexport function selectPrimaryMetric(\n\tmetrics: Map<string, number> | Record<string, number>,\n\tmetricName: string,\n): number | null {\n\tconst value = metrics instanceof Map ? metrics.get(metricName) : metrics[metricName];\n\treturn normalizeFiniteNumber(value);\n}\n\nexport function compareMetric(\n\tcurrent: number,\n\tbest: number | null | undefined,\n\tdirection: MetricDirection = \"lower\",\n): MetricComparison {\n\tconst normalizedBest = normalizeFiniteNumber(best) ?? null;\n\tif (normalizedBest === null) {\n\t\treturn {\n\t\t\tcurrent,\n\t\t\tbest: null,\n\t\t\tdirection,\n\t\t\tdelta: Number.POSITIVE_INFINITY,\n\t\t\trelativeDelta: null,\n\t\t\timproved: true,\n\t\t};\n\t}\n\n\tconst delta = direction === \"lower\" ? normalizedBest - current : current - normalizedBest;\n\tconst relativeDelta = normalizedBest === 0 ? null : delta / Math.abs(normalizedBest);\n\treturn {\n\t\tcurrent,\n\t\tbest: normalizedBest,\n\t\tdirection,\n\t\tdelta,\n\t\trelativeDelta,\n\t\timproved: delta > 0,\n\t};\n}\n\nexport function median(values: number[]): number | null {\n\tconst finite = values.filter(Number.isFinite).sort((a, b) => a - b);\n\tif (finite.length === 0) return null;\n\tconst mid = Math.floor(finite.length / 2);\n\treturn finite.length % 2 === 0 ? (finite[mid - 1] + finite[mid]) / 2 : finite[mid];\n}\n\nexport function medianAbsoluteDeviation(values: number[]): number | null {\n\tconst m = median(values);\n\tif (m === null) return null;\n\treturn median(values.filter(Number.isFinite).map((value) => Math.abs(value - m)));\n}\n\nexport function computeMadConfidence(improvementDelta: number, samples: number[]): ConfidenceResult {\n\tconst finite = samples.filter(Number.isFinite);\n\tif (finite.length < 3) {\n\t\treturn { mode: \"mad\", value: null, noiseFloor: null, sampleCount: finite.length };\n\t}\n\tconst mad = medianAbsoluteDeviation(finite);\n\tif (mad === null || mad === 0) {\n\t\treturn { mode: \"mad\", value: null, noiseFloor: mad, sampleCount: finite.length };\n\t}\n\treturn {\n\t\tmode: \"mad\",\n\t\tvalue: Math.abs(improvementDelta) / mad,\n\t\tnoiseFloor: mad,\n\t\tsampleCount: finite.length,\n\t};\n}\n\nexport function decideImprovement(input: ImprovementDecisionInput): ImprovementDecisionResult {\n\tconst direction = normalizeDirection(input.direction);\n\tconst minDelta = normalizeNonNegative(input.minDelta, 0);\n\tconst minRelativeDelta = normalizeNonNegative(input.minRelativeDelta, 0);\n\tconst minConfidence = normalizeNonNegative(input.minConfidence, 0);\n\tconst confidenceMode = input.confidenceMode ?? (minConfidence > 0 ? \"mad\" : \"none\");\n\tconst lowConfidenceAction = input.lowConfidenceAction ?? \"retry\";\n\n\tconst emptyConfidence: ConfidenceResult = { mode: confidenceMode, value: null, noiseFloor: null, sampleCount: 0 };\n\n\tif (input.checksPass === false) {\n\t\treturn { decision: \"discard\", reason: \"checks_failed\", comparison: null, confidence: emptyConfidence };\n\t}\n\n\tconst current = normalizeFiniteNumber(input.currentMetric);\n\tif (current === null) {\n\t\treturn {\n\t\t\tdecision: \"blocked\",\n\t\t\treason: input.currentMetric == null ? \"metric_missing\" : \"metric_invalid\",\n\t\t\tcomparison: null,\n\t\t\tconfidence: emptyConfidence,\n\t\t};\n\t}\n\n\tconst best = normalizeFiniteNumber(input.bestMetric) ?? null;\n\tconst comparison = compareMetric(current, best, direction);\n\tif (best === null) {\n\t\treturn { decision: \"keep\", reason: \"baseline\", comparison, confidence: emptyConfidence };\n\t}\n\tif (!comparison.improved) {\n\t\treturn { decision: \"discard\", reason: \"not_better_than_best\", comparison, confidence: emptyConfidence };\n\t}\n\tif (comparison.delta < minDelta) {\n\t\treturn { decision: \"discard\", reason: \"below_min_delta\", comparison, confidence: emptyConfidence };\n\t}\n\tif (comparison.relativeDelta !== null && comparison.relativeDelta < minRelativeDelta) {\n\t\treturn { decision: \"discard\", reason: \"below_min_relative_delta\", comparison, confidence: emptyConfidence };\n\t}\n\n\tif (confidenceMode === \"mad\" && minConfidence > 0) {\n\t\tconst samples = [...(input.noiseMetrics ?? []), current];\n\t\tconst confidence = computeMadConfidence(comparison.delta, samples);\n\t\tif (confidence.value === null) {\n\t\t\treturn {\n\t\t\t\tdecision: lowConfidenceAction,\n\t\t\t\treason: \"insufficient_noise_evidence\",\n\t\t\t\tcomparison,\n\t\t\t\tconfidence,\n\t\t\t};\n\t\t}\n\t\tif (confidence.value < minConfidence) {\n\t\t\treturn {\n\t\t\t\tdecision: lowConfidenceAction,\n\t\t\t\treason: \"below_confidence\",\n\t\t\t\tcomparison,\n\t\t\t\tconfidence,\n\t\t\t};\n\t\t}\n\t\treturn { decision: \"keep\", reason: \"metric_improved\", comparison, confidence };\n\t}\n\n\treturn { decision: \"keep\", reason: \"metric_improved\", comparison, confidence: emptyConfidence };\n}\n\nexport function serializeRunRecord(record: ImprovementRunRecord): string {\n\treturn JSON.stringify(record);\n}\n\nexport async function appendRunRecord(filePath: string, record: ImprovementRunRecord): Promise<void> {\n\tawait mkdir(dirname(filePath), { recursive: true });\n\tawait writeFile(filePath, `${serializeRunRecord(record)}\\n`, { flag: \"a\" });\n}\n\nexport async function readRunRecords(filePath: string): Promise<ImprovementRunRecord[]> {\n\tlet text = \"\";\n\ttry {\n\t\ttext = await readFile(filePath, \"utf8\");\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n\t\tthrow error;\n\t}\n\tconst records: ImprovementRunRecord[] = [];\n\tfor (const line of text.split(\"\\n\")) {\n\t\tif (!line.trim()) continue;\n\t\tconst parsed = JSON.parse(line) as ImprovementRunRecord;\n\t\trecords.push(parsed);\n\t}\n\treturn records;\n}\n\nfunction normalizeLoopId(loopId: string | undefined): string {\n\tconst normalized = (loopId ?? \"default\")\n\t\t.trim()\n\t\t.replace(/[^a-zA-Z0-9._-]+/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\");\n\treturn normalized || \"default\";\n}\n\nfunction workspaceKeyFor(cwd: string): string {\n\treturn createHash(\"sha256\").update(cwd).digest(\"hex\").slice(0, 16);\n}\n\nexport function improvementLoopPaths(input: ImprovementLoopStatusInput): ImprovementLoopPaths {\n\tconst rootDir = join(input.agentDir ?? getAgentDir(), \"improvement-loop\");\n\tconst workspaceKey = workspaceKeyFor(input.cwd);\n\tconst loopId = normalizeLoopId(input.loopId);\n\tconst workspaceDir = join(rootDir, \"workspaces\", workspaceKey);\n\treturn {\n\t\trootDir,\n\t\tworkspaceDir,\n\t\tlogPath: join(workspaceDir, `${loopId}.jsonl`),\n\t\tsandboxDir: join(workspaceDir, \"sandboxes\", loopId),\n\t\tartifactDir: join(workspaceDir, \"artifacts\", loopId),\n\t\tworkspaceKey,\n\t\tloopId,\n\t};\n}\n\nfunction finiteRunMetric(run: ImprovementRunRecord): number | null {\n\treturn normalizeFiniteNumber(run.metric);\n}\n\nfunction betterRun(\n\tcurrent: ImprovementRunRecord,\n\tbest: ImprovementRunRecord | null,\n\tdirection: MetricDirection,\n): ImprovementRunRecord {\n\tif (!best) return current;\n\tconst currentMetric = finiteRunMetric(current);\n\tconst bestMetric = finiteRunMetric(best);\n\tif (currentMetric === null || bestMetric === null) return best;\n\treturn compareMetric(currentMetric, bestMetric, direction).improved ? current : best;\n}\n\nfunction configFromInit(input: ImprovementLoopInitInput, loopId: string): ImprovementLoopConfig {\n\treturn {\n\t\tloopId,\n\t\tobjective: input.objective,\n\t\tmetricName: input.metricName,\n\t\tmetricUnit: input.metricUnit ?? \"\",\n\t\tdirection: normalizeDirection(input.direction),\n\t\tminDelta: normalizeNonNegative(input.minDelta, 0),\n\t\tminRelativeDelta: normalizeNonNegative(input.minRelativeDelta, 0),\n\t\tconfidenceMode: input.confidenceMode ?? (normalizeNonNegative(input.minConfidence, 0) > 0 ? \"mad\" : \"none\"),\n\t\tminConfidence: normalizeNonNegative(input.minConfidence, 0),\n\t\tlowConfidenceAction: input.lowConfidenceAction ?? \"retry\",\n\t\tcreatedAt: Date.now(),\n\t\tcwd: input.cwd,\n\t};\n}\n\nasync function readLoopLogEntries(logPath: string): Promise<ImprovementLoopLogEntry[]> {\n\tlet text = \"\";\n\ttry {\n\t\ttext = await readFile(logPath, \"utf8\");\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n\t\tthrow error;\n\t}\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.filter((line) => line.trim().length > 0)\n\t\t.map((line) => JSON.parse(line) as ImprovementLoopLogEntry);\n}\n\nfunction reconstructImprovementLoopState(\n\tentries: ImprovementLoopLogEntry[],\n\tlogPath: string,\n): ImprovementLoopState | null {\n\tlet config: ImprovementLoopConfig | null = null;\n\tlet runs: ImprovementRunRecord[] = [];\n\tconst sandboxById = new Map<string, ImprovementSandboxRecord>();\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"config\") {\n\t\t\tconst { type: _type, ...rest } = entry;\n\t\t\tconfig = rest;\n\t\t\truns = [];\n\t\t\tsandboxById.clear();\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.type === \"run\") {\n\t\t\tconst { type: _type, ...run } = entry;\n\t\t\truns.push(run);\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.type === \"sandbox\") {\n\t\t\tconst { type: _type, ...sandbox } = entry;\n\t\t\tsandboxById.set(sandbox.sandboxId, sandbox);\n\t\t}\n\t}\n\tif (!config) return null;\n\n\tconst baselineMetric = finiteRunMetric(runs[0] ?? ({} as ImprovementRunRecord));\n\tlet bestRun: ImprovementRunRecord | null = null;\n\tfor (const run of runs) {\n\t\tif (run.decision !== \"keep\" || finiteRunMetric(run) === null) continue;\n\t\tbestRun = betterRun(run, bestRun, config.direction);\n\t}\n\n\tconst sandboxes = [...sandboxById.values()].sort((a, b) => a.createdAt - b.createdAt);\n\tconst activeSandbox = [...sandboxes].reverse().find((sandbox) => sandbox.status === \"active\") ?? null;\n\treturn {\n\t\tconfig,\n\t\truns,\n\t\tsandboxes,\n\t\tactiveSandbox,\n\t\tbaselineMetric,\n\t\tbestMetric: bestRun ? finiteRunMetric(bestRun) : null,\n\t\tbestRunId: bestRun?.runId ?? null,\n\t\tlastDecision: null,\n\t\tlogPath,\n\t};\n}\n\nexport async function initImprovementLoop(input: ImprovementLoopInitInput): Promise<ImprovementLoopState> {\n\tconst paths = improvementLoopPaths(input);\n\tconst existing = await readLoopLogEntries(paths.logPath);\n\tif (existing.length > 0 && !input.reset) {\n\t\tthrow new Error(\n\t\t\t`Improvement loop already exists at ${paths.logPath}; pass reset=true to replace user-level loop state.`,\n\t\t);\n\t}\n\tconst config = configFromInit(input, paths.loopId);\n\tawait mkdir(dirname(paths.logPath), { recursive: true });\n\tawait writeFile(paths.logPath, `${JSON.stringify({ type: \"config\", ...config })}\\n`);\n\tconst state = reconstructImprovementLoopState([{ type: \"config\", ...config }], paths.logPath);\n\tif (!state) throw new Error(\"Failed to initialize improvement loop state\");\n\treturn state;\n}\n\nexport async function readImprovementLoopState(\n\tinput: ImprovementLoopStatusInput,\n): Promise<ImprovementLoopState | null> {\n\tconst paths = improvementLoopPaths(input);\n\treturn reconstructImprovementLoopState(await readLoopLogEntries(paths.logPath), paths.logPath);\n}\n\nfunction createSandboxId(input: ImprovementSandboxCreateInput): string {\n\tif (input.sandboxId) return normalizeLoopId(input.sandboxId);\n\tconst seed = `${input.cwd}:${input.loopId ?? \"default\"}:${Date.now()}:${Math.random()}`;\n\treturn `${Date.now()}-${createHash(\"sha256\").update(seed).digest(\"hex\").slice(0, 8)}`;\n}\n\nasync function appendSandboxRecord(\n\tinput: ImprovementLoopStatusInput,\n\tsandbox: ImprovementSandboxRecord,\n): Promise<ImprovementLoopState> {\n\tconst paths = improvementLoopPaths(input);\n\tawait mkdir(dirname(paths.logPath), { recursive: true });\n\tawait writeFile(paths.logPath, `${JSON.stringify({ type: \"sandbox\", ...sandbox })}\\n`, { flag: \"a\" });\n\tconst updated = await readImprovementLoopState(input);\n\tif (!updated) throw new Error(\"Failed to read improvement loop after sandbox update\");\n\treturn updated;\n}\n\nasync function runGit(\n\texec: ImprovementLoopExec,\n\tcwd: string,\n\targs: string[],\n\tsignal?: AbortSignal,\n): Promise<ImprovementMeasurementCommandResult> {\n\treturn exec(\"git\", args, { cwd, timeout: 60_000, signal, maxBuffer: 64 * 1024 });\n}\n\nexport async function createImprovementSandbox(input: ImprovementSandboxCreateInput): Promise<ImprovementLoopState> {\n\tconst state = await readImprovementLoopState(input);\n\tif (!state) throw new Error(\"Improvement loop is not initialized; call init first.\");\n\tif (state.activeSandbox) {\n\t\tthrow new Error(\n\t\t\t`Active sandbox already exists at ${state.activeSandbox.worktreePath}; clean it before creating another.`,\n\t\t);\n\t}\n\tconst status = await runGit(input.exec, input.cwd, [\"status\", \"--porcelain\"], input.signal);\n\tif (status.code !== 0) throw new Error(`Cannot inspect git status: ${(status.stderr || status.stdout).trim()}`);\n\tif (status.stdout.trim() && !input.allowDirtyRepo) {\n\t\tthrow new Error(\n\t\t\t\"Refusing to create sandbox from dirty repository; commit/stash changes or pass allowDirtyRepo=true.\",\n\t\t);\n\t}\n\tconst baseRef = input.baseRef?.trim() || \"HEAD\";\n\tconst sandboxId = createSandboxId(input);\n\tconst paths = improvementLoopPaths(input);\n\tconst worktreePath = join(paths.sandboxDir, sandboxId);\n\tawait mkdir(dirname(worktreePath), { recursive: true });\n\tconst add = await runGit(\n\t\tinput.exec,\n\t\tinput.cwd,\n\t\t[\"worktree\", \"add\", \"--detach\", worktreePath, baseRef],\n\t\tinput.signal,\n\t);\n\tif (add.code !== 0) throw new Error(`Failed to create git worktree sandbox: ${(add.stderr || add.stdout).trim()}`);\n\tconst sandbox: ImprovementSandboxRecord = {\n\t\tsandboxId,\n\t\tstatus: \"active\",\n\t\trepoPath: input.cwd,\n\t\tworktreePath,\n\t\tbaseRef,\n\t\tcreatedAt: Date.now(),\n\t};\n\treturn appendSandboxRecord(input, sandbox);\n}\n\nexport async function exportImprovementSandboxPatch(\n\tinput: ImprovementSandboxExportInput,\n): Promise<ImprovementLoopState> {\n\tconst state = await readImprovementLoopState(input);\n\tif (!state) throw new Error(\"Improvement loop is not initialized; call init first.\");\n\tconst sandbox = input.sandboxId\n\t\t? state.sandboxes.find((candidate) => candidate.sandboxId === normalizeLoopId(input.sandboxId))\n\t\t: state.activeSandbox;\n\tif (!sandbox || sandbox.status !== \"active\") throw new Error(\"No active sandbox found to export.\");\n\tconst diff = await runGit(input.exec, sandbox.worktreePath, [\"diff\", \"--binary\", \"HEAD\"], input.signal);\n\tif (diff.code !== 0) throw new Error(`Failed to export sandbox patch: ${(diff.stderr || diff.stdout).trim()}`);\n\tif (!diff.stdout.trim() && !input.allowEmptyPatch) throw new Error(\"Sandbox has no changes to export.\");\n\tconst paths = improvementLoopPaths(input);\n\tawait mkdir(paths.artifactDir, { recursive: true });\n\tconst patchPath = join(paths.artifactDir, `${sandbox.sandboxId}.patch`);\n\tawait writeFile(patchPath, diff.stdout);\n\treturn appendSandboxRecord(input, {\n\t\t...sandbox,\n\t\texportedAt: Date.now(),\n\t\tpatchPath,\n\t\tpatchBytes: Buffer.byteLength(diff.stdout),\n\t});\n}\n\nexport async function cleanupImprovementSandbox(input: ImprovementSandboxCleanupInput): Promise<ImprovementLoopState> {\n\tconst state = await readImprovementLoopState(input);\n\tif (!state) throw new Error(\"Improvement loop is not initialized; call init first.\");\n\tconst sandbox = input.sandboxId\n\t\t? state.sandboxes.find((candidate) => candidate.sandboxId === normalizeLoopId(input.sandboxId))\n\t\t: state.activeSandbox;\n\tif (!sandbox || sandbox.status !== \"active\") throw new Error(\"No active sandbox found to clean up.\");\n\tconst remove = await runGit(\n\t\tinput.exec,\n\t\tsandbox.repoPath,\n\t\t[\"worktree\", \"remove\", \"--force\", sandbox.worktreePath],\n\t\tinput.signal,\n\t);\n\tif (remove.code !== 0)\n\t\tthrow new Error(`Failed to remove git worktree sandbox: ${(remove.stderr || remove.stdout).trim()}`);\n\tawait runGit(input.exec, sandbox.repoPath, [\"worktree\", \"prune\"], input.signal);\n\treturn appendSandboxRecord(input, {\n\t\t...sandbox,\n\t\tstatus: \"cleaned\",\n\t\tcleanedAt: Date.now(),\n\t\treason: input.reason,\n\t});\n}\n\nexport async function recordImprovementRun(input: ImprovementLoopRecordInput): Promise<ImprovementLoopState> {\n\tconst state = await readImprovementLoopState(input);\n\tif (!state) throw new Error(\"Improvement loop is not initialized; call init first.\");\n\tconst runId = input.runId ?? state.runs.length + 1;\n\tconst priorMetrics = state.runs\n\t\t.map((run) => run.metric)\n\t\t.filter((metric): metric is number => Number.isFinite(metric));\n\tconst decision = decideImprovement({\n\t\tcurrentMetric: input.metric,\n\t\tbestMetric: state.bestMetric,\n\t\tdirection: state.config.direction,\n\t\tchecksPass: input.checksPass ?? null,\n\t\tminDelta: state.config.minDelta,\n\t\tminRelativeDelta: state.config.minRelativeDelta,\n\t\tconfidenceMode: state.config.confidenceMode,\n\t\tminConfidence: state.config.minConfidence,\n\t\tlowConfidenceAction: state.config.lowConfidenceAction,\n\t\tnoiseMetrics: priorMetrics,\n\t});\n\tconst record: ImprovementRunRecord = {\n\t\trunId,\n\t\tobjective: state.config.objective,\n\t\thypothesis: input.hypothesis,\n\t\tmetricName: state.config.metricName,\n\t\tmetricUnit: state.config.metricUnit,\n\t\tdirection: state.config.direction,\n\t\tmetric: normalizeFiniteNumber(input.metric),\n\t\tsecondaryMetrics: input.secondaryMetrics,\n\t\tchecksPass: input.checksPass ?? null,\n\t\tdecision: decision.decision,\n\t\treason: decision.reason,\n\t\tconfidence: decision.confidence.value,\n\t\tchangedFiles: input.changedFiles,\n\t\tevidenceRef: input.evidenceRef,\n\t\tnextHint: input.nextHint,\n\t\ttimestamp: Date.now(),\n\t};\n\tawait mkdir(dirname(state.logPath), { recursive: true });\n\tawait writeFile(state.logPath, `${JSON.stringify({ type: \"run\", ...record })}\\n`, { flag: \"a\" });\n\tconst updated = await readImprovementLoopState(input);\n\tif (!updated) throw new Error(\"Failed to read improvement loop after recording run\");\n\treturn { ...updated, lastDecision: decision };\n}\n\nfunction secondaryMetricsFrom(\n\tparsedMetrics: Record<string, number>,\n\tprimaryName: string,\n): Record<string, number> | undefined {\n\tconst secondary = Object.fromEntries(Object.entries(parsedMetrics).filter(([name]) => name !== primaryName));\n\treturn Object.keys(secondary).length > 0 ? secondary : undefined;\n}\n\nexport async function runImprovementMeasurement(input: {\n\texec: ImprovementLoopExec;\n\tcwd: string;\n\tcommand: string;\n\tmetricName: string;\n\tchecksCommand?: string;\n\ttimeoutSeconds?: number;\n\tchecksTimeoutSeconds?: number;\n\tmaxOutputBytes?: number;\n\tsignal?: AbortSignal;\n}): Promise<ImprovementMeasurementResult> {\n\tconst command = input.command.trim();\n\tif (!command) throw new Error(\"command is required\");\n\tconst timeoutMs = Math.max(1, Math.floor(input.timeoutSeconds ?? 600)) * 1000;\n\tconst checksTimeoutMs = Math.max(1, Math.floor(input.checksTimeoutSeconds ?? 300)) * 1000;\n\tconst maxBuffer = Math.max(1024, Math.floor(input.maxOutputBytes ?? 64 * 1024));\n\tconst started = Date.now();\n\tconst result = await input.exec(\"bash\", [\"-c\", command], {\n\t\tcwd: input.cwd,\n\t\ttimeout: timeoutMs,\n\t\tsignal: input.signal,\n\t\tmaxBuffer,\n\t});\n\tconst durationMs = Date.now() - started;\n\tconst output = `${result.stdout}\\n${result.stderr}`;\n\tconst parsedMetrics = metricMapFromOutput(output);\n\tconst primaryMetric = selectPrimaryMetric(parsedMetrics, input.metricName);\n\tlet checksPass = result.code === 0 && !result.killed;\n\tlet checksExitCode: number | undefined;\n\tlet checksTimedOut: boolean | undefined;\n\tlet checksStdout: string | undefined;\n\tlet checksStderr: string | undefined;\n\tconst checksCommand = input.checksCommand?.trim();\n\tif (checksPass && checksCommand) {\n\t\tconst checks = await input.exec(\"bash\", [\"-c\", checksCommand], {\n\t\t\tcwd: input.cwd,\n\t\t\ttimeout: checksTimeoutMs,\n\t\t\tsignal: input.signal,\n\t\t\tmaxBuffer,\n\t\t});\n\t\tchecksExitCode = checks.code;\n\t\tchecksTimedOut = checks.killed;\n\t\tchecksStdout = checks.stdout;\n\t\tchecksStderr = checks.stderr;\n\t\tchecksPass = checks.code === 0 && !checks.killed;\n\t}\n\n\treturn {\n\t\tcommand,\n\t\texitCode: result.code,\n\t\ttimedOut: result.killed,\n\t\tdurationMs,\n\t\tstdout: result.stdout,\n\t\tstderr: result.stderr,\n\t\tstdoutTruncated: !!result.stdoutTruncated,\n\t\tstderrTruncated: !!result.stderrTruncated,\n\t\tparsedMetrics,\n\t\tprimaryMetric,\n\t\tchecksCommand: checksCommand || undefined,\n\t\tchecksExitCode,\n\t\tchecksTimedOut,\n\t\tchecksPass,\n\t\tchecksStdout,\n\t\tchecksStderr,\n\t};\n}\n\nexport function parseGitPorcelainStatus(status: string): GitStatusEntry[] {\n\treturn status\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trimEnd())\n\t\t.filter(Boolean)\n\t\t.map((line) => {\n\t\t\tconst index = line[0] ?? \" \";\n\t\t\tconst workingTree = line[1] ?? \" \";\n\t\t\tconst rawPath = line.slice(3);\n\t\t\tconst renameParts = rawPath.split(\" -> \");\n\t\t\tif (renameParts.length === 2) {\n\t\t\t\treturn { index, workingTree, origPath: renameParts[0], path: renameParts[1] };\n\t\t\t}\n\t\t\treturn { index, workingTree, path: rawPath };\n\t\t});\n}\n\nfunction entriesFromStatus(status: string | GitStatusEntry[]): GitStatusEntry[] {\n\treturn Array.isArray(status) ? status : parseGitPorcelainStatus(status);\n}\n\nfunction normalizePath(path: string): string {\n\treturn path.replace(/\\\\/g, \"/\").replace(/^\\.\\//, \"\");\n}\n\nfunction pathSet(paths: string[]): Set<string> {\n\treturn new Set(paths.map(normalizePath).filter(Boolean));\n}\n\nfunction entryPaths(entry: GitStatusEntry): string[] {\n\treturn [entry.path, entry.origPath].filter((value): value is string => typeof value === \"string\").map(normalizePath);\n}\n\nfunction statusPathSet(entries: GitStatusEntry[]): Set<string> {\n\tconst paths = new Set<string>();\n\tfor (const entry of entries) {\n\t\tfor (const path of entryPaths(entry)) paths.add(path);\n\t}\n\treturn paths;\n}\n\nfunction isPreserved(path: string, preservePaths: Set<string>): boolean {\n\tfor (const preserve of preservePaths) {\n\t\tif (path === preserve || path.startsWith(`${preserve}/`)) return true;\n\t}\n\treturn false;\n}\n\nexport function planOwnedDiscard(input: OwnedDiscardPlanInput): OwnedDiscardPlan {\n\tconst beforePaths = statusPathSet(entriesFromStatus(input.beforeStatus));\n\tconst afterPaths = statusPathSet(entriesFromStatus(input.afterStatus));\n\tconst ownedPaths = pathSet(input.ownedPaths);\n\tconst preservePaths = pathSet(input.preservePaths ?? []);\n\tconst revertPaths: string[] = [];\n\tconst protectedUserDirtyPaths: string[] = [];\n\tconst unownedChangedPaths: string[] = [];\n\tconst preservedChangedPaths: string[] = [];\n\n\tfor (const path of afterPaths) {\n\t\tif (isPreserved(path, preservePaths)) {\n\t\t\tpreservedChangedPaths.push(path);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!ownedPaths.has(path)) {\n\t\t\tunownedChangedPaths.push(path);\n\t\t\tcontinue;\n\t\t}\n\t\tif (beforePaths.has(path)) {\n\t\t\tprotectedUserDirtyPaths.push(path);\n\t\t\tcontinue;\n\t\t}\n\t\trevertPaths.push(path);\n\t}\n\n\treturn {\n\t\trevertPaths: [...new Set(revertPaths)].sort(),\n\t\tpreservePaths: [...new Set(preservedChangedPaths)].sort(),\n\t\tprotectedUserDirtyPaths: [...new Set(protectedUserDirtyPaths)].sort(),\n\t\tunownedChangedPaths: [...new Set(unownedChangedPaths)].sort(),\n\t\tcanDiscardOwnedChanges: protectedUserDirtyPaths.length === 0,\n\t};\n}\n\nconst DecisionToolParams = Type.Object(\n\t{\n\t\tcurrentMetric: Type.Number({ description: \"Measured primary metric for the candidate run.\" }),\n\t\tbestMetric: Type.Optional(Type.Number({ description: \"Best kept primary metric so far. Omit for baseline.\" })),\n\t\tdirection: StringEnum([\"lower\", \"higher\"] as const, {\n\t\t\tdescription: \"Whether lower or higher metric values are better. Default lower.\",\n\t\t}),\n\t\tchecksPass: Type.Optional(\n\t\t\tType.Boolean({ description: \"Whether correctness checks passed. false forces discard.\" }),\n\t\t),\n\t\tminDelta: Type.Optional(\n\t\t\tType.Number({ description: \"Minimum absolute improvement required to keep. Default 0.\" }),\n\t\t),\n\t\tminRelativeDelta: Type.Optional(\n\t\t\tType.Number({ description: \"Minimum relative improvement required to keep, e.g. 0.01 for 1%. Default 0.\" }),\n\t\t),\n\t\tconfidenceMode: Type.Optional(\n\t\t\tStringEnum([\"none\", \"mad\"] as const, {\n\t\t\t\tdescription: \"Noise/confidence policy. Use mad with minConfidence to reject noisy wins.\",\n\t\t\t}),\n\t\t),\n\t\tminConfidence: Type.Optional(\n\t\t\tType.Number({ description: \"Minimum confidence multiple over MAD noise floor. Default 0.\" }),\n\t\t),\n\t\tlowConfidenceAction: Type.Optional(\n\t\t\tStringEnum([\"retry\", \"discard\"] as const, {\n\t\t\t\tdescription: \"Decision when confidence evidence is missing or too low. Default retry.\",\n\t\t\t}),\n\t\t),\n\t\tnoiseMetrics: Type.Optional(\n\t\t\tType.Array(Type.Number(), { description: \"Previous metric samples for MAD confidence calculation.\" }),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nexport function createImprovementDecisionTool(): ToolDefinition<typeof DecisionToolParams, ImprovementDecisionResult> {\n\treturn defineTool({\n\t\tname: \"improvement_decision\",\n\t\tlabel: \"Improvement Decision\",\n\t\tdescription:\n\t\t\t\"Deterministically decide keep/discard/retry/blocked for an improvement candidate from metric, direction, thresholds, checks, and optional noise samples. The model proposes; code judges.\",\n\t\tpromptSnippet: \"Decide keep/discard for a measured improvement candidate using deterministic metric gates\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use improvement_decision after measuring a candidate when keep/discard must be based on metric evidence rather than model intuition.\",\n\t\t\t\"Do not claim a candidate is kept unless this tool returns decision=keep or a stronger project-specific validator passes.\",\n\t\t\t\"Correctness failures override metric wins; checksPass=false always discards.\",\n\t\t],\n\t\tparameters: DecisionToolParams,\n\t\tasync execute(_toolCallId, params) {\n\t\t\tconst decision = decideImprovement(params);\n\t\t\tconst comparison = decision.comparison;\n\t\t\tconst confidenceText = decision.confidence.value === null ? \"n/a\" : decision.confidence.value.toFixed(2);\n\t\t\tconst deltaText = comparison ? comparison.delta.toString() : \"n/a\";\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Decision: ${decision.decision} (${decision.reason})\\nDelta: ${deltaText}\\nConfidence: ${confidenceText}`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdetails: decision,\n\t\t\t};\n\t\t},\n\t});\n}\n\nconst LoopToolParams = Type.Object(\n\t{\n\t\taction: StringEnum(\n\t\t\t[\"init\", \"status\", \"record\", \"measure\", \"sandbox_create\", \"sandbox_export\", \"sandbox_cleanup\"] as const,\n\t\t\t{\n\t\t\t\tdescription:\n\t\t\t\t\t\"init creates user-level loop state, status reads it, record appends a supplied measurement, measure runs and records a bounded command, sandbox_create/export/cleanup manage disposable git worktrees and keep patches.\",\n\t\t\t},\n\t\t),\n\t\tloopId: Type.Optional(Type.String({ description: \"Loop id within the current workspace. Default: default.\" })),\n\t\tobjective: Type.Optional(Type.String({ description: \"Loop objective. Required for action=init.\" })),\n\t\tmetricName: Type.Optional(Type.String({ description: \"Primary metric name. Required for action=init.\" })),\n\t\tmetricUnit: Type.Optional(Type.String({ description: \"Display unit for the primary metric.\" })),\n\t\tdirection: Type.Optional(\n\t\t\tStringEnum([\"lower\", \"higher\"] as const, {\n\t\t\t\tdescription: \"Whether lower or higher metric values are better. Default lower.\",\n\t\t\t}),\n\t\t),\n\t\treset: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription: \"For action=init, replace existing user-level loop state for this workspace/loop id.\",\n\t\t\t}),\n\t\t),\n\t\tminDelta: Type.Optional(\n\t\t\tType.Number({ description: \"Minimum absolute improvement required to keep. Default 0.\" }),\n\t\t),\n\t\tminRelativeDelta: Type.Optional(\n\t\t\tType.Number({ description: \"Minimum relative improvement required to keep. Default 0.\" }),\n\t\t),\n\t\tconfidenceMode: Type.Optional(\n\t\t\tStringEnum([\"none\", \"mad\"] as const, {\n\t\t\t\tdescription: \"Noise/confidence policy. Default none unless minConfidence > 0.\",\n\t\t\t}),\n\t\t),\n\t\tminConfidence: Type.Optional(\n\t\t\tType.Number({ description: \"Minimum confidence multiple over MAD noise floor. Default 0.\" }),\n\t\t),\n\t\tlowConfidenceAction: Type.Optional(\n\t\t\tStringEnum([\"retry\", \"discard\"] as const, {\n\t\t\t\tdescription: \"Decision when confidence evidence is missing or low. Default retry.\",\n\t\t\t}),\n\t\t),\n\t\tcurrentMetric: Type.Optional(Type.Number({ description: \"Measured primary metric for action=record.\" })),\n\t\tchecksPass: Type.Optional(\n\t\t\tType.Boolean({ description: \"Correctness check result for action=record. false forces discard.\" }),\n\t\t),\n\t\thypothesis: Type.Optional(Type.String({ description: \"What this run tried.\" })),\n\t\tsecondaryMetrics: Type.Optional(\n\t\t\tType.Object(\n\t\t\t\t{},\n\t\t\t\t{ additionalProperties: Type.Number(), description: \"Secondary metric map for action=record.\" },\n\t\t\t),\n\t\t),\n\t\tchangedFiles: Type.Optional(Type.Array(Type.String(), { description: \"Files changed by this candidate run.\" })),\n\t\tevidenceRef: Type.Optional(\n\t\t\tType.String({ description: \"Path, command, or artifact reference proving the measurement/check result.\" }),\n\t\t),\n\t\tnextHint: Type.Optional(\n\t\t\tType.String({ description: \"Useful next-step hint for later runs, especially after discard/retry/blocked.\" }),\n\t\t),\n\t\tcommand: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Measurement command for action=measure. Runs via bash -c in ctx.cwd or active sandbox when useSandbox=true.\",\n\t\t\t}),\n\t\t),\n\t\tchecksCommand: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Optional correctness command for action=measure. Runs only if the measurement command exits 0.\",\n\t\t\t}),\n\t\t),\n\t\ttimeoutSeconds: Type.Optional(\n\t\t\tType.Number({ description: \"Measurement command timeout in seconds. Default 600.\" }),\n\t\t),\n\t\tchecksTimeoutSeconds: Type.Optional(\n\t\t\tType.Number({ description: \"Checks command timeout in seconds. Default 300.\" }),\n\t\t),\n\t\tmaxOutputBytes: Type.Optional(\n\t\t\tType.Number({ description: \"Maximum stdout/stderr tail retained per command. Default 65536.\" }),\n\t\t),\n\t\tuseSandbox: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription: \"For action=measure, run command/checks inside the active sandbox worktree. Default false.\",\n\t\t\t}),\n\t\t),\n\t\tsandboxId: Type.Optional(\n\t\t\tType.String({\n\t\t\t\tdescription:\n\t\t\t\t\t\"Sandbox id for sandbox_create/sandbox_export/sandbox_cleanup. Defaults to generated id or active sandbox.\",\n\t\t\t}),\n\t\t),\n\t\tbaseRef: Type.Optional(Type.String({ description: \"Git base ref for sandbox_create. Default HEAD.\" })),\n\t\tallowDirtyRepo: Type.Optional(\n\t\t\tType.Boolean({\n\t\t\t\tdescription: \"Allow sandbox_create when the real repo has uncommitted changes. Default false.\",\n\t\t\t}),\n\t\t),\n\t\tcleanupReason: Type.Optional(\n\t\t\tType.String({ description: \"Optional reason recorded when sandbox_cleanup removes a sandbox.\" }),\n\t\t),\n\t\tallowEmptyPatch: Type.Optional(\n\t\t\tType.Boolean({ description: \"For sandbox_export, allow writing an empty patch. Default false.\" }),\n\t\t),\n\t},\n\t{ additionalProperties: false },\n);\n\nfunction requireString(value: string | undefined, label: string): string {\n\tconst trimmed = value?.trim();\n\tif (!trimmed) throw new Error(`${label} is required`);\n\treturn trimmed;\n}\n\nfunction summarizeLoopState(state: ImprovementLoopState | null): string {\n\tif (!state) return \"Improvement loop is not initialized.\";\n\tconst lines = [\n\t\t`Loop ${state.config.loopId}: ${state.config.objective}`,\n\t\t`Metric: ${state.config.metricName} (${state.config.metricUnit || \"unitless\"}, ${state.config.direction} is better)`,\n\t\t`Runs: ${state.runs.length}`,\n\t\t`Baseline: ${state.baselineMetric ?? \"none\"}`,\n\t\t`Best: ${state.bestMetric ?? \"none\"}${state.bestRunId === null ? \"\" : ` (#${state.bestRunId})`}`,\n\t\t`Log: ${state.logPath}`,\n\t];\n\tconst lastRun = state.runs.at(-1);\n\tif (lastRun) lines.push(`Last: ${lastRun.decision} (${lastRun.reason}) metric=${lastRun.metric ?? \"missing\"}`);\n\tif (state.activeSandbox) lines.push(`Active sandbox: ${state.activeSandbox.worktreePath}`);\n\treturn lines.join(\"\\n\");\n}\n\nexport function createImprovementLoopTool(\n\texec?: ImprovementLoopExec,\n): ToolDefinition<typeof LoopToolParams, ImprovementLoopToolDetails> {\n\treturn defineTool({\n\t\tname: \"improvement_loop\",\n\t\tlabel: \"Improvement Loop\",\n\t\tdescription:\n\t\t\t\"Persist and evaluate a deterministic improvement loop in user-level state: init objective/metric policy, measure bounded commands, record measured runs, manage disposable git worktree sandboxes, and status current baseline/best/decision log. Does not commit/apply/revert real-repo files.\",\n\t\tpromptSnippet: \"Track deterministic improvement-loop state and record measured keep/discard decisions\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use improvement_loop init before iterative optimization that needs metric-based keep/discard state.\",\n\t\t\t\"Use improvement_loop measure to run a bounded measurement command and optional checks command, then record the deterministic decision.\",\n\t\t\t\"Use improvement_loop sandbox_create before risky self-modifying experiments so edits happen in a disposable git worktree, not the real repository.\",\n\t\t\t\"Use improvement_loop sandbox_export to capture the sandbox diff as a user-level patch artifact before cleanup when the decision is keep.\",\n\t\t\t\"Use improvement_loop sandbox_cleanup after discard or after exporting/approving a patch; cleanup removes the disposable worktree record, not the real repository.\",\n\t\t\t\"Use improvement_loop record when measurement already happened elsewhere; code decides keep/discard/retry/blocked from metrics and gates.\",\n\t\t\t\"This tool does not commit/apply/revert real-repo files; keep/discard is a decision record until a later approved executor applies it.\",\n\t\t\t\"Operational state is stored under the user-level agent directory, not in the target repository.\",\n\t\t],\n\t\tparameters: LoopToolParams,\n\t\texecutionMode: \"sequential\",\n\t\tasync execute(_toolCallId, params, signal, _onUpdate, ctx) {\n\t\t\tconst cwd = ctx.cwd;\n\t\t\tif (params.action === \"init\") {\n\t\t\t\tconst state = await initImprovementLoop({\n\t\t\t\t\tcwd,\n\t\t\t\t\tloopId: params.loopId,\n\t\t\t\t\tobjective: requireString(params.objective, \"objective\"),\n\t\t\t\t\tmetricName: requireString(params.metricName, \"metricName\"),\n\t\t\t\t\tmetricUnit: params.metricUnit,\n\t\t\t\t\tdirection: params.direction,\n\t\t\t\t\tminDelta: params.minDelta,\n\t\t\t\t\tminRelativeDelta: params.minRelativeDelta,\n\t\t\t\t\tconfidenceMode: params.confidenceMode,\n\t\t\t\t\tminConfidence: params.minConfidence,\n\t\t\t\t\tlowConfidenceAction: params.lowConfidenceAction,\n\t\t\t\t\treset: params.reset,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Initialized improvement loop.\\n${summarizeLoopState(state)}` }],\n\t\t\t\t\tdetails: { action: \"init\", state, logPath: state.logPath },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (params.action === \"record\") {\n\t\t\t\tconst state = await recordImprovementRun({\n\t\t\t\t\tcwd,\n\t\t\t\t\tloopId: params.loopId,\n\t\t\t\t\tmetric: params.currentMetric,\n\t\t\t\t\tsecondaryMetrics: params.secondaryMetrics as Record<string, number> | undefined,\n\t\t\t\t\tchecksPass: params.checksPass ?? null,\n\t\t\t\t\thypothesis: params.hypothesis,\n\t\t\t\t\tchangedFiles: params.changedFiles,\n\t\t\t\t\tevidenceRef: params.evidenceRef,\n\t\t\t\t\tnextHint: params.nextHint,\n\t\t\t\t});\n\t\t\t\tconst decision = state.lastDecision;\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Recorded run: ${decision?.decision ?? \"unknown\"} (${decision?.reason ?? \"unknown\"}).\\n${summarizeLoopState(state)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { action: \"record\", state, decision: decision ?? undefined, logPath: state.logPath },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (params.action === \"measure\") {\n\t\t\t\tif (!exec) throw new Error(\"improvement_loop measure requires createImprovementLoopTool(pi.exec)\");\n\t\t\t\tconst before = await readImprovementLoopState({ cwd, loopId: params.loopId });\n\t\t\t\tif (!before) throw new Error(\"Improvement loop is not initialized; call init first.\");\n\t\t\t\tconst measurementCwd = params.useSandbox\n\t\t\t\t\t? (before.activeSandbox?.worktreePath ??\n\t\t\t\t\t\t(() => {\n\t\t\t\t\t\t\tthrow new Error(\"useSandbox=true requires an active sandbox.\");\n\t\t\t\t\t\t})())\n\t\t\t\t\t: cwd;\n\t\t\t\tconst measurement = await runImprovementMeasurement({\n\t\t\t\t\texec,\n\t\t\t\t\tcwd: measurementCwd,\n\t\t\t\t\tcommand: requireString(params.command, \"command\"),\n\t\t\t\t\tmetricName: before.config.metricName,\n\t\t\t\t\tchecksCommand: params.checksCommand,\n\t\t\t\t\ttimeoutSeconds: params.timeoutSeconds,\n\t\t\t\t\tchecksTimeoutSeconds: params.checksTimeoutSeconds,\n\t\t\t\t\tmaxOutputBytes: params.maxOutputBytes,\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t\tconst state = await recordImprovementRun({\n\t\t\t\t\tcwd,\n\t\t\t\t\tloopId: params.loopId,\n\t\t\t\t\tmetric: measurement.primaryMetric,\n\t\t\t\t\tsecondaryMetrics: secondaryMetricsFrom(measurement.parsedMetrics, before.config.metricName),\n\t\t\t\t\tchecksPass: measurement.checksPass,\n\t\t\t\t\thypothesis: params.hypothesis,\n\t\t\t\t\tchangedFiles: params.changedFiles,\n\t\t\t\t\tevidenceRef: params.evidenceRef ?? `command:${measurement.command};cwd:${measurementCwd}`,\n\t\t\t\t\tnextHint: params.nextHint,\n\t\t\t\t});\n\t\t\t\tconst decision = state.lastDecision;\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Measured ${before.config.metricName}=${measurement.primaryMetric ?? \"missing\"}; decision: ${decision?.decision ?? \"unknown\"} (${decision?.reason ?? \"unknown\"}).\\n${summarizeLoopState(state)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\taction: \"measure\",\n\t\t\t\t\t\tstate,\n\t\t\t\t\t\tdecision: decision ?? undefined,\n\t\t\t\t\t\tmeasurement,\n\t\t\t\t\t\tlogPath: state.logPath,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (params.action === \"sandbox_create\") {\n\t\t\t\tif (!exec) throw new Error(\"improvement_loop sandbox_create requires createImprovementLoopTool(pi.exec)\");\n\t\t\t\tconst state = await createImprovementSandbox({\n\t\t\t\t\tcwd,\n\t\t\t\t\tloopId: params.loopId,\n\t\t\t\t\texec,\n\t\t\t\t\tbaseRef: params.baseRef,\n\t\t\t\t\tallowDirtyRepo: params.allowDirtyRepo,\n\t\t\t\t\tsandboxId: params.sandboxId,\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t\tconst sandbox = state.activeSandbox ?? undefined;\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Created sandbox: ${sandbox?.worktreePath ?? \"unknown\"}\\n${summarizeLoopState(state)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { action: \"sandbox_create\", state, sandbox, logPath: state.logPath },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (params.action === \"sandbox_export\") {\n\t\t\t\tif (!exec) throw new Error(\"improvement_loop sandbox_export requires createImprovementLoopTool(pi.exec)\");\n\t\t\t\tconst state = await exportImprovementSandboxPatch({\n\t\t\t\t\tcwd,\n\t\t\t\t\tloopId: params.loopId,\n\t\t\t\t\texec,\n\t\t\t\t\tsandboxId: params.sandboxId,\n\t\t\t\t\tallowEmptyPatch: params.allowEmptyPatch,\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t\tconst sandbox = params.sandboxId\n\t\t\t\t\t? state.sandboxes.find((candidate) => candidate.sandboxId === normalizeLoopId(params.sandboxId))\n\t\t\t\t\t: (state.activeSandbox ?? state.sandboxes.at(-1));\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Exported sandbox patch: ${sandbox?.patchPath ?? \"unknown\"}\\n${summarizeLoopState(state)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { action: \"sandbox_export\", state, sandbox, logPath: state.logPath },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (params.action === \"sandbox_cleanup\") {\n\t\t\t\tif (!exec) throw new Error(\"improvement_loop sandbox_cleanup requires createImprovementLoopTool(pi.exec)\");\n\t\t\t\tconst state = await cleanupImprovementSandbox({\n\t\t\t\t\tcwd,\n\t\t\t\t\tloopId: params.loopId,\n\t\t\t\t\texec,\n\t\t\t\t\tsandboxId: params.sandboxId,\n\t\t\t\t\treason: params.cleanupReason,\n\t\t\t\t\tsignal,\n\t\t\t\t});\n\t\t\t\tconst sandbox = params.sandboxId\n\t\t\t\t\t? state.sandboxes.find((candidate) => candidate.sandboxId === normalizeLoopId(params.sandboxId))\n\t\t\t\t\t: state.sandboxes.at(-1);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Cleaned sandbox.\\n${summarizeLoopState(state)}` }],\n\t\t\t\t\tdetails: { action: \"sandbox_cleanup\", state, sandbox, logPath: state.logPath },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst state = await readImprovementLoopState({ cwd, loopId: params.loopId });\n\t\t\tconst paths = improvementLoopPaths({ cwd, loopId: params.loopId });\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: summarizeLoopState(state) }],\n\t\t\t\tdetails: { action: \"status\", state, logPath: state?.logPath ?? paths.logPath },\n\t\t\t};\n\t\t},\n\t});\n}\n"]}
|