fifony 0.1.47 → 0.1.48
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/README.md +78 -0
- package/app/dist/assets/CommandPalette-CZDG20HW.js +1 -0
- package/app/dist/assets/{KeyboardShortcutsHelp-CqEFfGcE.js → KeyboardShortcutsHelp-TYhQc4aA.js} +1 -1
- package/app/dist/assets/OnboardingWizard-CQ9YmVIT.js +1 -0
- package/app/dist/assets/agents.lazy-CgakDm_P.js +1 -0
- package/app/dist/assets/analytics.lazy-C0rw3sov.js +1 -0
- package/app/dist/assets/{createLucideIcon-luywpIq4.js → createLucideIcon-B3bah5lk.js} +1 -1
- package/app/dist/assets/hooks-CNPue7d7.js +1 -0
- package/app/dist/assets/index-B8XCmr0-.css +1 -0
- package/app/dist/assets/index-Dfn02uW3.js +47 -0
- package/app/dist/assets/index.lazy-JdqhBwPd.js +44 -0
- package/app/dist/assets/services-CHpVij2M.css +1 -0
- package/app/dist/assets/services.lazy-BShKUCOt.js +12 -0
- package/app/dist/assets/vendor-X6HTElZW.js +9 -0
- package/app/dist/assets/viz-Dsh_q2DK.js +4 -0
- package/app/dist/index.html +5 -4
- package/app/dist/service-worker.js +32 -1
- package/dist/agent/run-local.js +89 -76
- package/dist/{agent-DFSFG6DG.js → agent-DJ4SCNBZ.js} +22 -17
- package/dist/{analytics-broadcaster-O4AE3RUK.js → analytics-broadcaster-INNYWHDJ.js} +25 -20
- package/dist/approve-plan.command-WE2CO3H2.js +21 -0
- package/dist/{chunk-HOIOVUHI.js → chunk-5M7PBFMZ.js} +8 -6
- package/dist/chunk-7R7XFXJM.js +1247 -0
- package/dist/{chunk-2PRRKBG6.js → chunk-A4P2MYJF.js} +22 -9
- package/dist/chunk-AFOV3ZAF.js +722 -0
- package/dist/chunk-AFP36N23.js +134 -0
- package/dist/{chunk-AAZKYWOY.js → chunk-AFYKGVSP.js} +103 -8
- package/dist/chunk-APJOZXRP.js +737 -0
- package/dist/chunk-DLSPRIQL.js +241 -0
- package/dist/{chunk-5AMWD66T.js → chunk-EDIPHR5B.js} +6 -4
- package/dist/{chunk-K36BWMUV.js → chunk-JU3MF3MW.js} +2526 -736
- package/dist/{chunk-7TXZYZR5.js → chunk-N5HCNY4O.js} +7 -5
- package/dist/{chunk-JRLWLZOD.js → chunk-NKMZYPIS.js} +31 -23
- package/dist/{chunk-PI7Y77R3.js → chunk-OFIVTM2E.js} +17 -7
- package/dist/{chunk-QH6VCTET.js → chunk-RCSJFMQG.js} +909 -98
- package/dist/{chunk-AAVROEQC.js → chunk-UR7T7IA6.js} +253 -349
- package/dist/{chunk-QHISYRXJ.js → chunk-VOYLU3MI.js} +57 -3
- package/dist/{chunk-EBCSQFPR.js → chunk-W5IULOWV.js} +2 -3
- package/dist/chunk-X37RNTWU.js +193 -0
- package/dist/{chunk-PACI3T4I.js → chunk-XY2APMDE.js} +13 -5
- package/dist/chunk-Z6ZWNWWR.js +34 -0
- package/dist/cli.js +45 -17
- package/dist/constants-AAP7ZGCX.js +124 -0
- package/dist/create-issue.command-SX3AXXIC.js +29 -0
- package/dist/fsm-agent-JGV22WK4.js +59 -0
- package/dist/{fsm-issue-EHTSKMFN.js → fsm-issue-LHIJM5VB.js} +12 -8
- package/dist/{fsm-service-7O4AJG2R.js → fsm-service-GGDKUTWS.js} +13 -4
- package/dist/{helpers-ON2S7UEF.js → helpers-AENVYEZJ.js} +6 -2
- package/dist/{issue-log-broadcaster-FZGVEEIX.js → issue-log-broadcaster-QQWM7LOV.js} +29 -18
- package/dist/{issues-3YNNTB4U.js → issues-RXFKKSXB.js} +10 -7
- package/dist/{log-analyzer-EIX6R6PP.js → log-analyzer-4LNXQISY.js} +30 -20
- package/dist/{logger-IFLXTQPS.js → logger-4F6ATWNA.js} +2 -1
- package/dist/mcp/server.js +6 -2
- package/dist/merge-workspace.command-ZNGIZC4O.js +29 -0
- package/dist/{parallel-executor-DWESCNX3.js → parallel-executor-OL5CB33L.js} +78 -19
- package/dist/{pid-manager-UBWXVSMD.js → pid-manager-EDT4DHAU.js} +2 -1
- package/dist/queue-workers-NSKIIMQ2.js +43 -0
- package/dist/replan-issue.command-73PETERX.js +21 -0
- package/dist/retry-issue.command-DIDP4OCS.js +21 -0
- package/dist/reverse-proxy-server-QSS3H4UH.js +97 -0
- package/dist/scheduler-5YORYECF.js +37 -0
- package/dist/service-log-broadcaster-JIUP2L3D.js +21 -0
- package/dist/{settings-SOTIS6ZD.js → settings-ZNDXYL46.js} +34 -23
- package/dist/settings.resource-OKUHXICJ.js +35 -0
- package/dist/{store-S3NAYZ3S.js → store-P3ACO6YA.js} +22 -17
- package/dist/telemetry-KVUFHDQS.js +828 -0
- package/dist/template-variants-HEPLYKMP.js +24 -0
- package/dist/trace-bundle-IJOV7IWH.js +41 -0
- package/dist/{web-push-QCTLS7EJ.js → web-push-X2LLMQ4M.js} +2 -1
- package/dist/websocket-Q2TUCIC2.js +103 -0
- package/dist/{workspace-OS7GPMCN.js → workspace-TDX3NJCX.js} +10 -6
- package/package.json +12 -9
- package/app/dist/assets/CommandPalette-CL8p78lG.js +0 -1
- package/app/dist/assets/OnboardingWizard-BmI50ZUv.js +0 -1
- package/app/dist/assets/analytics.lazy-CXGjZabc.js +0 -1
- package/app/dist/assets/index-CEaccpYh.js +0 -96
- package/app/dist/assets/index-CzzWGzux.css +0 -1
- package/app/dist/assets/vendor-uqBx3VSC.js +0 -9
- package/dist/approve-plan.command-QGQZZXTQ.js +0 -17
- package/dist/chunk-N4KFNX2G.js +0 -370
- package/dist/chunk-VM5QAYP5.js +0 -404
- package/dist/create-issue.command-VAKYRECC.js +0 -24
- package/dist/merge-workspace.command-T2NIGR4M.js +0 -24
- package/dist/queue-workers-V57BYXAY.js +0 -38
- package/dist/replan-issue.command-2GQ3QXCR.js +0 -17
- package/dist/retry-issue.command-GJBUUYDJ.js +0 -17
- package/dist/scheduler-KYILMWLD.js +0 -32
- package/dist/settings.resource-JMD3JQOS.js +0 -30
- package/dist/websocket-T2Y3BY4B.js +0 -61
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
import {
|
|
2
|
+
init_helpers,
|
|
3
|
+
now
|
|
4
|
+
} from "./chunk-DLSPRIQL.js";
|
|
5
|
+
import {
|
|
6
|
+
STATE_ROOT,
|
|
7
|
+
init_constants
|
|
8
|
+
} from "./chunk-X37RNTWU.js";
|
|
9
|
+
import {
|
|
10
|
+
logger
|
|
11
|
+
} from "./chunk-PXTIWKLQ.js";
|
|
12
|
+
import {
|
|
13
|
+
__require
|
|
14
|
+
} from "./chunk-Z6ZWNWWR.js";
|
|
15
|
+
|
|
16
|
+
// src/domains/trace-bundle.ts
|
|
17
|
+
import { execSync } from "child_process";
|
|
18
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync, writeFileSync } from "fs";
|
|
19
|
+
import { dirname, join as join2, relative } from "path";
|
|
20
|
+
init_helpers();
|
|
21
|
+
|
|
22
|
+
// src/agents/transcript.ts
|
|
23
|
+
init_constants();
|
|
24
|
+
import { mkdirSync, appendFileSync, readFileSync, existsSync } from "fs";
|
|
25
|
+
import { join } from "path";
|
|
26
|
+
var TRANSCRIPTS_ROOT = join(STATE_ROOT, "transcripts");
|
|
27
|
+
function transcriptDir(issueId) {
|
|
28
|
+
return join(TRANSCRIPTS_ROOT, issueId);
|
|
29
|
+
}
|
|
30
|
+
function transcriptFile(issueId, planVersion, attempt) {
|
|
31
|
+
return join(transcriptDir(issueId), `v${planVersion}a${attempt}.jsonl`);
|
|
32
|
+
}
|
|
33
|
+
function recordTranscriptTurn(issue, turn, provider) {
|
|
34
|
+
try {
|
|
35
|
+
const dir = transcriptDir(issue.id);
|
|
36
|
+
mkdirSync(dir, { recursive: true });
|
|
37
|
+
const pv = issue.planVersion ?? 1;
|
|
38
|
+
const ea = issue.executeAttempt ?? 1;
|
|
39
|
+
const filePath = transcriptFile(issue.id, pv, ea);
|
|
40
|
+
const outputPreview = turn.output.length > 500 ? turn.output.slice(-500) : turn.output;
|
|
41
|
+
const entry = {
|
|
42
|
+
ts: turn.completedAt || turn.startedAt,
|
|
43
|
+
turn: turn.turn,
|
|
44
|
+
role: turn.role || "unknown",
|
|
45
|
+
model: turn.model,
|
|
46
|
+
provider,
|
|
47
|
+
directiveStatus: turn.directiveStatus,
|
|
48
|
+
directiveSummary: turn.directiveSummary,
|
|
49
|
+
exitCode: turn.code,
|
|
50
|
+
success: turn.success,
|
|
51
|
+
tokens: turn.tokenUsage ? {
|
|
52
|
+
input: turn.tokenUsage.inputTokens,
|
|
53
|
+
output: turn.tokenUsage.outputTokens,
|
|
54
|
+
total: turn.tokenUsage.totalTokens
|
|
55
|
+
} : void 0,
|
|
56
|
+
toolsUsed: turn.toolsUsed,
|
|
57
|
+
skillsUsed: turn.skillsUsed,
|
|
58
|
+
agentsUsed: turn.agentsUsed,
|
|
59
|
+
commandsRun: turn.commandsRun,
|
|
60
|
+
outputPreview
|
|
61
|
+
};
|
|
62
|
+
appendFileSync(filePath, JSON.stringify(entry) + "\n", "utf8");
|
|
63
|
+
} catch (err) {
|
|
64
|
+
logger.warn({ err: String(err), issueId: issue.id }, "[Transcript] Failed to record turn");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function readTranscript(issueId, planVersion, attempt) {
|
|
68
|
+
const filePath = transcriptFile(issueId, planVersion, attempt);
|
|
69
|
+
if (!existsSync(filePath)) return [];
|
|
70
|
+
try {
|
|
71
|
+
const lines = readFileSync(filePath, "utf8").trim().split("\n").filter(Boolean);
|
|
72
|
+
return lines.map((line) => JSON.parse(line));
|
|
73
|
+
} catch (err) {
|
|
74
|
+
logger.warn({ err: String(err), issueId }, "[Transcript] Failed to read transcript");
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function readAllTranscripts(issueId) {
|
|
79
|
+
const dir = transcriptDir(issueId);
|
|
80
|
+
if (!existsSync(dir)) return {};
|
|
81
|
+
try {
|
|
82
|
+
const { readdirSync: readdirSync2 } = __require("fs");
|
|
83
|
+
const files = readdirSync2(dir).filter((f) => f.endsWith(".jsonl"));
|
|
84
|
+
const result = {};
|
|
85
|
+
for (const file of files) {
|
|
86
|
+
const key = file.replace(".jsonl", "");
|
|
87
|
+
const lines = readFileSync(join(dir, file), "utf8").trim().split("\n").filter(Boolean);
|
|
88
|
+
result[key] = lines.map((line) => JSON.parse(line));
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
} catch (err) {
|
|
92
|
+
logger.warn({ err: String(err), issueId }, "[Transcript] Failed to read all transcripts");
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function getTranscriptSummary(issueId) {
|
|
97
|
+
const all = readAllTranscripts(issueId);
|
|
98
|
+
const attempts = Object.entries(all).map(([key, entries]) => {
|
|
99
|
+
const totalTokens = entries.reduce((sum, e) => sum + (e.tokens?.total ?? 0), 0);
|
|
100
|
+
const last = entries[entries.length - 1];
|
|
101
|
+
return {
|
|
102
|
+
key,
|
|
103
|
+
turns: entries.length,
|
|
104
|
+
totalTokens,
|
|
105
|
+
success: last?.success ?? false,
|
|
106
|
+
lastRole: last?.role
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
return { attempts };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/domains/trace-bundle.ts
|
|
113
|
+
var TRACE_DIR = "traces";
|
|
114
|
+
var ATTEMPT_MANIFEST_FILE = "attempt.json";
|
|
115
|
+
var TRANSCRIPT_MIRROR_FILE = "transcript.jsonl";
|
|
116
|
+
var CHANGED_FILES_FILE = "changed-files.json";
|
|
117
|
+
var DIFF_STAT_FILE = "diff.stat";
|
|
118
|
+
var DIFF_PATCH_FILE = "diff.patch";
|
|
119
|
+
var CHECKPOINT_FILE = "checkpoint.json";
|
|
120
|
+
var HANDOFF_FILE = "handoff.md";
|
|
121
|
+
var RAILS_FILE = "rails.json";
|
|
122
|
+
var SIMILAR_TRACES_FILE = "similar-traces.json";
|
|
123
|
+
function traceManifestPath(traceDirectory) {
|
|
124
|
+
return join2(traceDirectory, ATTEMPT_MANIFEST_FILE);
|
|
125
|
+
}
|
|
126
|
+
function traceTranscriptPath(traceDirectory) {
|
|
127
|
+
return join2(traceDirectory, TRANSCRIPT_MIRROR_FILE);
|
|
128
|
+
}
|
|
129
|
+
function traceTurnsPath(traceDirectory) {
|
|
130
|
+
return join2(traceDirectory, "turns");
|
|
131
|
+
}
|
|
132
|
+
function safeReadChangedFiles(traceDirectory) {
|
|
133
|
+
try {
|
|
134
|
+
const raw = readFileSync2(join2(traceDirectory, CHANGED_FILES_FILE), "utf8");
|
|
135
|
+
const parsed = JSON.parse(raw);
|
|
136
|
+
return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
|
|
137
|
+
} catch {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function safeReadDiffStatExcerpt(traceDirectory) {
|
|
142
|
+
try {
|
|
143
|
+
return readFileSync2(join2(traceDirectory, DIFF_STAT_FILE), "utf8").split("\n").map((line) => line.trimEnd()).filter(Boolean).slice(0, 6);
|
|
144
|
+
} catch {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function safeReadJsonFile(path) {
|
|
149
|
+
try {
|
|
150
|
+
return JSON.parse(readFileSync2(path, "utf8"));
|
|
151
|
+
} catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function findLastDirectiveArtifact(traceDirectory) {
|
|
156
|
+
const turnsDir = traceTurnsPath(traceDirectory);
|
|
157
|
+
if (!existsSync2(turnsDir)) return void 0;
|
|
158
|
+
try {
|
|
159
|
+
const directives = readdirSync(turnsDir).filter((entry) => entry.endsWith(".directive.json")).sort((left, right) => left.localeCompare(right));
|
|
160
|
+
return directives.length > 0 ? `turns/${directives[directives.length - 1]}` : void 0;
|
|
161
|
+
} catch {
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function collectReviewBlockers(issue) {
|
|
166
|
+
const criteria = issue.gradingReport?.criteria ?? [];
|
|
167
|
+
return criteria.filter((criterion) => criterion.result === "FAIL" && criterion.blocking).slice(0, 5).map((criterion) => ({
|
|
168
|
+
id: criterion.id,
|
|
169
|
+
category: criterion.category,
|
|
170
|
+
description: criterion.description,
|
|
171
|
+
evidence: criterion.evidence
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
function deriveRemainingWork(issue, patch, changedFiles, reviewBlockers) {
|
|
175
|
+
const items = [];
|
|
176
|
+
if (reviewBlockers.length > 0) {
|
|
177
|
+
items.push(`Address ${reviewBlockers.length} blocking review criteria before resubmitting.`);
|
|
178
|
+
}
|
|
179
|
+
if (patch.outcome === "failure" || patch.outcome === "timeout" || patch.outcome === "crash") {
|
|
180
|
+
if (changedFiles.length > 0) {
|
|
181
|
+
items.push("Inspect `diff.patch` and `changed-files.json` before retrying the same implementation path.");
|
|
182
|
+
} else {
|
|
183
|
+
items.push("The previous attempt left no workspace diff. Take a materially different execution strategy.");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (patch.nextIssueState === "Blocked") {
|
|
187
|
+
items.push("Unblock the current execution path or adjust the plan before resuming.");
|
|
188
|
+
}
|
|
189
|
+
const priorSuggestion = issue.previousAttemptSummaries?.[issue.previousAttemptSummaries.length - 1]?.insight?.suggestion;
|
|
190
|
+
if (priorSuggestion) {
|
|
191
|
+
items.push(`Prior heuristic suggestion: ${priorSuggestion}`);
|
|
192
|
+
}
|
|
193
|
+
return Array.from(new Set(items)).slice(0, 5);
|
|
194
|
+
}
|
|
195
|
+
function buildRailsArtifact(worktreePath, issue, manifest) {
|
|
196
|
+
const relevantDecisions = (issue.policyDecisions ?? []).filter((decision) => (decision.planVersion ?? manifest.planVersion) === manifest.planVersion).slice(0, 8).map((decision) => ({
|
|
197
|
+
id: decision.id,
|
|
198
|
+
kind: decision.kind,
|
|
199
|
+
scope: decision.scope,
|
|
200
|
+
basis: decision.basis,
|
|
201
|
+
from: decision.from,
|
|
202
|
+
to: decision.to,
|
|
203
|
+
rationale: decision.rationale,
|
|
204
|
+
recordedAt: decision.recordedAt
|
|
205
|
+
}));
|
|
206
|
+
return {
|
|
207
|
+
generatedAt: now(),
|
|
208
|
+
issue: {
|
|
209
|
+
id: issue.id,
|
|
210
|
+
identifier: issue.identifier
|
|
211
|
+
},
|
|
212
|
+
attempt: {
|
|
213
|
+
planVersion: manifest.planVersion,
|
|
214
|
+
executeAttempt: manifest.executeAttempt,
|
|
215
|
+
role: manifest.role,
|
|
216
|
+
provider: manifest.provider
|
|
217
|
+
},
|
|
218
|
+
harness: {
|
|
219
|
+
mode: issue.plan?.harnessMode ?? "standard",
|
|
220
|
+
checkpointPolicy: issue.plan?.executionContract?.checkpointPolicy ?? "final_only",
|
|
221
|
+
checkpointStatus: issue.checkpointStatus,
|
|
222
|
+
contractNegotiationStatus: issue.contractNegotiationStatus
|
|
223
|
+
},
|
|
224
|
+
budget: {
|
|
225
|
+
retryBudget: {
|
|
226
|
+
used: issue.attempts ?? 0,
|
|
227
|
+
max: issue.maxAttempts ?? 0,
|
|
228
|
+
remaining: Math.max(0, (issue.maxAttempts ?? 0) - (issue.attempts ?? 0))
|
|
229
|
+
},
|
|
230
|
+
executeAttempt: issue.executeAttempt ?? manifest.executeAttempt,
|
|
231
|
+
reviewAttempt: issue.reviewAttempt ?? 0,
|
|
232
|
+
checkpointAttempt: issue.checkpointAttempt ?? 0,
|
|
233
|
+
contractNegotiationAttempt: issue.contractNegotiationAttempt ?? 0,
|
|
234
|
+
budgetPolicy: issue.plan?.executionContract?.budgetPolicy ?? null
|
|
235
|
+
},
|
|
236
|
+
runtimeRails: {
|
|
237
|
+
contextResetCount: issue.contextResetCount ?? 0,
|
|
238
|
+
lastHandoffFile: issue.lastHandoffFile ? relative(worktreePath, issue.lastHandoffFile) : void 0,
|
|
239
|
+
lastFailedPhase: issue.lastFailedPhase
|
|
240
|
+
},
|
|
241
|
+
policyDecisions: relevantDecisions
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function finalizeSimilarTraceSelectionArtifact(traceDirectory, worktreePath, patch) {
|
|
245
|
+
const artifactPath = join2(traceDirectory, SIMILAR_TRACES_FILE);
|
|
246
|
+
if (!existsSync2(artifactPath)) return {};
|
|
247
|
+
try {
|
|
248
|
+
const artifact = safeReadJsonFile(artifactPath);
|
|
249
|
+
if (!artifact) return {};
|
|
250
|
+
const changedFiles = safeReadChangedFiles(traceDirectory);
|
|
251
|
+
let likelyFollowedHit = null;
|
|
252
|
+
let bestOverlapScore = 0;
|
|
253
|
+
for (const hit of artifact.hits ?? []) {
|
|
254
|
+
const relativeAttemptPath = hit.files?.attempt;
|
|
255
|
+
if (!relativeAttemptPath) continue;
|
|
256
|
+
const candidateAttemptPath = join2(worktreePath, relativeAttemptPath);
|
|
257
|
+
const candidateChangedFilesPath = join2(dirname(candidateAttemptPath), CHANGED_FILES_FILE);
|
|
258
|
+
const candidateChangedFiles = safeReadJsonFile(candidateChangedFilesPath);
|
|
259
|
+
const candidateFiles = Array.isArray(candidateChangedFiles) ? candidateChangedFiles.filter((value) => typeof value === "string" && value.length > 0) : [];
|
|
260
|
+
const overlapFiles = candidateFiles.filter((file) => changedFiles.includes(file));
|
|
261
|
+
if (overlapFiles.length === 0) continue;
|
|
262
|
+
const overlapScore = overlapFiles.length * 10 + (hit.score ?? 0);
|
|
263
|
+
if (overlapScore <= bestOverlapScore) continue;
|
|
264
|
+
bestOverlapScore = overlapScore;
|
|
265
|
+
likelyFollowedHit = {
|
|
266
|
+
issueId: hit.issueId ?? "",
|
|
267
|
+
issueIdentifier: hit.issueIdentifier ?? "",
|
|
268
|
+
score: hit.score ?? 0,
|
|
269
|
+
overlapFiles,
|
|
270
|
+
reasons: hit.reasons ?? []
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
artifact.postAttemptAnalysis = {
|
|
274
|
+
analyzedAt: now(),
|
|
275
|
+
attemptOutcome: patch.outcome,
|
|
276
|
+
attemptChangedFiles: changedFiles,
|
|
277
|
+
likelyFollowedHit
|
|
278
|
+
};
|
|
279
|
+
writeFileSync(artifactPath, `${JSON.stringify(artifact, null, 2)}
|
|
280
|
+
`, "utf8");
|
|
281
|
+
return { similarTraces: SIMILAR_TRACES_FILE };
|
|
282
|
+
} catch (error) {
|
|
283
|
+
logger.warn({ error: String(error), traceDirectory }, "[TraceBundle] Failed to finalize similar trace selection artifact");
|
|
284
|
+
return {};
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function writeRailsArtifact(traceDirectory, worktreePath, issue) {
|
|
288
|
+
const manifest = readExistingManifest(traceDirectory);
|
|
289
|
+
if (!manifest) return {};
|
|
290
|
+
try {
|
|
291
|
+
const rails = buildRailsArtifact(worktreePath, issue, manifest);
|
|
292
|
+
writeFileSync(join2(traceDirectory, RAILS_FILE), `${JSON.stringify(rails, null, 2)}
|
|
293
|
+
`, "utf8");
|
|
294
|
+
return { rails: RAILS_FILE };
|
|
295
|
+
} catch (error) {
|
|
296
|
+
logger.warn({ error: String(error), traceDirectory, issueId: issue.id }, "[TraceBundle] Failed to write rails artifact");
|
|
297
|
+
return {};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function renderHandoffMarkdown(checkpoint) {
|
|
301
|
+
const lines = [
|
|
302
|
+
"# Attempt Handoff",
|
|
303
|
+
"",
|
|
304
|
+
`- Issue: ${checkpoint.issue.identifier} \u2014 ${checkpoint.issue.title}`,
|
|
305
|
+
`- Attempt: v${checkpoint.attempt.planVersion}a${checkpoint.attempt.executeAttempt}`,
|
|
306
|
+
`- Outcome: ${checkpoint.attempt.outcome ?? "unknown"}`,
|
|
307
|
+
`- Next state: ${checkpoint.attempt.nextIssueState ?? "unknown"}`,
|
|
308
|
+
"",
|
|
309
|
+
"## Resume Order",
|
|
310
|
+
"- Start with `handoff.md` for the concise summary.",
|
|
311
|
+
"- Read `checkpoint.json` for structured resume state.",
|
|
312
|
+
"- Open `attempt.json`, the last `directive.json`, and `diff.patch` only if they are relevant to the failure mode.",
|
|
313
|
+
""
|
|
314
|
+
];
|
|
315
|
+
if (checkpoint.changedFiles.length > 0) {
|
|
316
|
+
lines.push("## Changed Files");
|
|
317
|
+
lines.push(...checkpoint.changedFiles.map((file) => `- \`${file}\``));
|
|
318
|
+
lines.push("");
|
|
319
|
+
}
|
|
320
|
+
if (checkpoint.diffStatExcerpt.length > 0) {
|
|
321
|
+
lines.push("## Diff Summary");
|
|
322
|
+
lines.push("```text");
|
|
323
|
+
lines.push(...checkpoint.diffStatExcerpt);
|
|
324
|
+
lines.push("```");
|
|
325
|
+
lines.push("");
|
|
326
|
+
}
|
|
327
|
+
if (checkpoint.lastTurn) {
|
|
328
|
+
lines.push("## Last Turn");
|
|
329
|
+
lines.push(`- Turn: ${checkpoint.lastTurn.turn}`);
|
|
330
|
+
if (checkpoint.lastTurn.directiveStatus) lines.push(`- Directive status: ${checkpoint.lastTurn.directiveStatus}`);
|
|
331
|
+
if (checkpoint.lastTurn.directiveSummary) lines.push(`- Directive summary: ${checkpoint.lastTurn.directiveSummary}`);
|
|
332
|
+
if (checkpoint.lastTurn.outputPreview) lines.push(`- Output preview: ${checkpoint.lastTurn.outputPreview}`);
|
|
333
|
+
lines.push("");
|
|
334
|
+
}
|
|
335
|
+
if (checkpoint.reviewBlockers.length > 0) {
|
|
336
|
+
lines.push("## Blocking Review Evidence");
|
|
337
|
+
for (const blocker of checkpoint.reviewBlockers) {
|
|
338
|
+
lines.push(`- **${blocker.id}**${blocker.category ? ` [${blocker.category}]` : ""}: ${blocker.description ?? "Blocking review failure"}`);
|
|
339
|
+
if (blocker.evidence) {
|
|
340
|
+
lines.push(` Evidence: ${blocker.evidence}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
lines.push("");
|
|
344
|
+
}
|
|
345
|
+
if (checkpoint.remainingWork.length > 0) {
|
|
346
|
+
lines.push("## Remaining Work");
|
|
347
|
+
lines.push(...checkpoint.remainingWork.map((item) => `- ${item}`));
|
|
348
|
+
lines.push("");
|
|
349
|
+
}
|
|
350
|
+
lines.push("## Key Artifacts");
|
|
351
|
+
lines.push(`- \`${checkpoint.resumeArtifacts.checkpoint}\``);
|
|
352
|
+
if (checkpoint.resumeArtifacts.rails) lines.push(`- \`${checkpoint.resumeArtifacts.rails}\``);
|
|
353
|
+
if (checkpoint.resumeArtifacts.similarTraces) lines.push(`- \`${checkpoint.resumeArtifacts.similarTraces}\``);
|
|
354
|
+
lines.push(`- \`${checkpoint.resumeArtifacts.attemptManifest}\``);
|
|
355
|
+
if (checkpoint.resumeArtifacts.lastDirective) lines.push(`- \`${checkpoint.resumeArtifacts.lastDirective}\``);
|
|
356
|
+
if (checkpoint.resumeArtifacts.diffPatch) lines.push(`- \`${checkpoint.resumeArtifacts.diffPatch}\``);
|
|
357
|
+
lines.push(`- \`${checkpoint.resumeArtifacts.transcript}\``);
|
|
358
|
+
lines.push(`- \`${checkpoint.resumeArtifacts.changedFiles}\``);
|
|
359
|
+
lines.push(`- \`${checkpoint.resumeArtifacts.diffStat}\``);
|
|
360
|
+
if (checkpoint.resumeArtifacts.bootstrap) lines.push(`- \`${checkpoint.resumeArtifacts.bootstrap}\``);
|
|
361
|
+
lines.push("");
|
|
362
|
+
return `${lines.join("\n")}
|
|
363
|
+
`;
|
|
364
|
+
}
|
|
365
|
+
function writeHandoffArtifacts(traceDirectory, issue, patch, transcriptEntries) {
|
|
366
|
+
const manifest = readExistingManifest(traceDirectory);
|
|
367
|
+
if (!manifest) return {};
|
|
368
|
+
try {
|
|
369
|
+
const changedFiles = safeReadChangedFiles(traceDirectory);
|
|
370
|
+
const reviewBlockers = collectReviewBlockers(issue);
|
|
371
|
+
const checkpoint = {
|
|
372
|
+
generatedAt: now(),
|
|
373
|
+
issue: {
|
|
374
|
+
id: issue.id,
|
|
375
|
+
identifier: issue.identifier,
|
|
376
|
+
title: issue.title
|
|
377
|
+
},
|
|
378
|
+
attempt: {
|
|
379
|
+
planVersion: manifest.planVersion,
|
|
380
|
+
executeAttempt: manifest.executeAttempt,
|
|
381
|
+
role: manifest.role,
|
|
382
|
+
provider: manifest.provider,
|
|
383
|
+
outcome: patch.outcome,
|
|
384
|
+
nextIssueState: patch.nextIssueState,
|
|
385
|
+
error: patch.error
|
|
386
|
+
},
|
|
387
|
+
lastTurn: transcriptEntries.length > 0 ? {
|
|
388
|
+
turn: transcriptEntries[transcriptEntries.length - 1].turn,
|
|
389
|
+
directiveStatus: transcriptEntries[transcriptEntries.length - 1].directiveStatus,
|
|
390
|
+
directiveSummary: transcriptEntries[transcriptEntries.length - 1].directiveSummary,
|
|
391
|
+
exitCode: transcriptEntries[transcriptEntries.length - 1].exitCode,
|
|
392
|
+
outputPreview: transcriptEntries[transcriptEntries.length - 1].outputPreview
|
|
393
|
+
} : null,
|
|
394
|
+
changedFiles,
|
|
395
|
+
diffStatExcerpt: safeReadDiffStatExcerpt(traceDirectory),
|
|
396
|
+
reviewBlockers,
|
|
397
|
+
priorAttemptSuggestion: issue.previousAttemptSummaries?.[issue.previousAttemptSummaries.length - 1]?.insight?.suggestion,
|
|
398
|
+
remainingWork: deriveRemainingWork(issue, patch, changedFiles, reviewBlockers),
|
|
399
|
+
resumeArtifacts: {
|
|
400
|
+
attemptManifest: ATTEMPT_MANIFEST_FILE,
|
|
401
|
+
checkpoint: CHECKPOINT_FILE,
|
|
402
|
+
handoff: HANDOFF_FILE,
|
|
403
|
+
rails: existsSync2(join2(traceDirectory, RAILS_FILE)) ? RAILS_FILE : void 0,
|
|
404
|
+
similarTraces: existsSync2(join2(traceDirectory, SIMILAR_TRACES_FILE)) ? SIMILAR_TRACES_FILE : void 0,
|
|
405
|
+
transcript: TRANSCRIPT_MIRROR_FILE,
|
|
406
|
+
changedFiles: CHANGED_FILES_FILE,
|
|
407
|
+
diffStat: DIFF_STAT_FILE,
|
|
408
|
+
diffPatch: existsSync2(join2(traceDirectory, DIFF_PATCH_FILE)) ? DIFF_PATCH_FILE : void 0,
|
|
409
|
+
bootstrap: existsSync2(join2(traceDirectory, "bootstrap.md")) ? "bootstrap.md" : void 0,
|
|
410
|
+
lastDirective: findLastDirectiveArtifact(traceDirectory)
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
writeFileSync(join2(traceDirectory, CHECKPOINT_FILE), `${JSON.stringify(checkpoint, null, 2)}
|
|
414
|
+
`, "utf8");
|
|
415
|
+
writeFileSync(join2(traceDirectory, HANDOFF_FILE), renderHandoffMarkdown(checkpoint), "utf8");
|
|
416
|
+
return { checkpoint: CHECKPOINT_FILE, handoff: HANDOFF_FILE };
|
|
417
|
+
} catch (error) {
|
|
418
|
+
logger.warn({ error: String(error), traceDirectory, issueId: issue.id }, "[TraceBundle] Failed to write handoff artifacts");
|
|
419
|
+
return {};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function traceDir(worktreePath, planVersion, executeAttempt) {
|
|
423
|
+
return join2(workspaceSafePath(worktreePath), TRACE_DIR, `v${planVersion}a${executeAttempt}`);
|
|
424
|
+
}
|
|
425
|
+
function safeReadFileSlice(filePath, maxChars) {
|
|
426
|
+
try {
|
|
427
|
+
if (!existsSync2(filePath)) return null;
|
|
428
|
+
const raw = readFileSync2(filePath, "utf8");
|
|
429
|
+
return raw.length > maxChars ? raw.slice(0, maxChars) + "\n[...truncated]" : raw;
|
|
430
|
+
} catch {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function extractCheckpointSummary(filePath, maxChars) {
|
|
435
|
+
try {
|
|
436
|
+
if (!existsSync2(filePath)) return null;
|
|
437
|
+
const raw = readFileSync2(filePath, "utf8");
|
|
438
|
+
const data = JSON.parse(raw);
|
|
439
|
+
const parts = [];
|
|
440
|
+
if (data.outcome) parts.push(`**Outcome:** ${data.outcome}`);
|
|
441
|
+
if (data.error) parts.push(`**Error:** ${String(data.error).slice(0, 200)}`);
|
|
442
|
+
if (Array.isArray(data.reviewBlockers) && data.reviewBlockers.length > 0) {
|
|
443
|
+
parts.push(`**Review blockers:** ${data.reviewBlockers.map((b) => `${b.id ?? "?"}: ${b.description ?? ""}`).join("; ")}`);
|
|
444
|
+
}
|
|
445
|
+
if (Array.isArray(data.remainingWork) && data.remainingWork.length > 0) {
|
|
446
|
+
parts.push(`**Remaining work:**
|
|
447
|
+
${data.remainingWork.map((w) => `- ${w}`).join("\n")}`);
|
|
448
|
+
}
|
|
449
|
+
if (data.lastTurn) {
|
|
450
|
+
const lt = data.lastTurn;
|
|
451
|
+
if (lt.summary) parts.push(`**Last turn:** ${lt.summary}`);
|
|
452
|
+
}
|
|
453
|
+
const result = parts.join("\n");
|
|
454
|
+
return result.length > maxChars ? result.slice(0, maxChars) + "\n[...truncated]" : result;
|
|
455
|
+
} catch {
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
function extractLastDirectiveSummary(traceDirectory, maxChars) {
|
|
460
|
+
const turnsDir = join2(traceDirectory, "turns");
|
|
461
|
+
try {
|
|
462
|
+
if (!existsSync2(turnsDir)) return null;
|
|
463
|
+
const directives = readdirSync(turnsDir).filter((f) => f.endsWith(".directive.json")).sort();
|
|
464
|
+
if (directives.length === 0) return null;
|
|
465
|
+
const last = readFileSync2(join2(turnsDir, directives[directives.length - 1]), "utf8");
|
|
466
|
+
const data = JSON.parse(last);
|
|
467
|
+
const parts = [];
|
|
468
|
+
if (data.directiveSummary || data.summary) parts.push(String(data.directiveSummary ?? data.summary));
|
|
469
|
+
if (data.outputPreview) parts.push(`Output: ${String(data.outputPreview).slice(0, 300)}`);
|
|
470
|
+
if (data.status) parts.push(`Status: ${data.status}`);
|
|
471
|
+
const result = parts.join("\n");
|
|
472
|
+
return result.length > maxChars ? result.slice(0, maxChars) + "\n[...truncated]" : result;
|
|
473
|
+
} catch {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
function readTraceContent(traceDirectory, budget) {
|
|
478
|
+
let remaining = budget;
|
|
479
|
+
let truncated = false;
|
|
480
|
+
const handoffBudget = Math.min(Math.floor(budget * 0.35), 2500);
|
|
481
|
+
const handoffMarkdown = safeReadFileSlice(join2(traceDirectory, "handoff.md"), handoffBudget);
|
|
482
|
+
if (handoffMarkdown) remaining -= handoffMarkdown.length;
|
|
483
|
+
const checkpointBudget = Math.min(Math.floor(budget * 0.2), 1500);
|
|
484
|
+
const checkpointSummary = remaining > 200 ? extractCheckpointSummary(join2(traceDirectory, "checkpoint.json"), Math.min(checkpointBudget, remaining)) : null;
|
|
485
|
+
if (checkpointSummary) remaining -= checkpointSummary.length;
|
|
486
|
+
const diffBudget = Math.min(Math.floor(budget * 0.3), remaining);
|
|
487
|
+
const diffPatch = remaining > 200 ? safeReadFileSlice(join2(traceDirectory, "diff.patch"), Math.min(diffBudget, remaining)) : null;
|
|
488
|
+
if (diffPatch) remaining -= diffPatch.length;
|
|
489
|
+
const directiveBudget = Math.min(Math.floor(budget * 0.15), remaining);
|
|
490
|
+
const lastDirectiveSummary = remaining > 100 ? extractLastDirectiveSummary(traceDirectory, Math.min(directiveBudget, remaining)) : null;
|
|
491
|
+
if (lastDirectiveSummary) remaining -= lastDirectiveSummary.length;
|
|
492
|
+
const totalChars = budget - remaining;
|
|
493
|
+
if (totalChars >= budget * 0.95) truncated = true;
|
|
494
|
+
return { handoffMarkdown, checkpointSummary, diffPatch, lastDirectiveSummary, totalChars, truncated };
|
|
495
|
+
}
|
|
496
|
+
function workspaceSafePath(workspacePath) {
|
|
497
|
+
return workspacePath.replace(/\\/g, "/");
|
|
498
|
+
}
|
|
499
|
+
function ensureTraceDir(worktreePath, planVersion, executeAttempt) {
|
|
500
|
+
const dir = traceDir(worktreePath, planVersion, executeAttempt);
|
|
501
|
+
try {
|
|
502
|
+
mkdirSync2(traceTurnsPath(dir), { recursive: true });
|
|
503
|
+
return dir;
|
|
504
|
+
} catch (error) {
|
|
505
|
+
logger.warn({ error: String(error), issueWorkspace: worktreePath, planVersion, executeAttempt }, "[TraceBundle] Failed to create trace directory");
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
function toTraceBundle(traceDirectory) {
|
|
510
|
+
return {
|
|
511
|
+
dir: traceDirectory,
|
|
512
|
+
manifestFile: ATTEMPT_MANIFEST_FILE,
|
|
513
|
+
transcriptFile: TRANSCRIPT_MIRROR_FILE,
|
|
514
|
+
turnsDir: "turns"
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
function readExistingManifest(traceDirectory) {
|
|
518
|
+
const path = traceManifestPath(traceDirectory);
|
|
519
|
+
if (!existsSync2(path)) return null;
|
|
520
|
+
try {
|
|
521
|
+
const raw = readFileSync2(path, "utf8");
|
|
522
|
+
const parsed = JSON.parse(raw);
|
|
523
|
+
return parsed;
|
|
524
|
+
} catch (error) {
|
|
525
|
+
logger.warn({ error: String(error), traceDirectory }, "[TraceBundle] Failed to read existing attempt manifest");
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function loadAttemptManifest(traceDirectory) {
|
|
530
|
+
return readExistingManifest(traceDirectory);
|
|
531
|
+
}
|
|
532
|
+
function writeAttemptManifest(traceDirectory, manifest) {
|
|
533
|
+
const path = traceManifestPath(traceDirectory);
|
|
534
|
+
try {
|
|
535
|
+
writeFileSync(path, JSON.stringify(manifest, null, 2), "utf8");
|
|
536
|
+
} catch (error) {
|
|
537
|
+
logger.warn({ error: String(error), traceDirectory }, "[TraceBundle] Failed to write attempt manifest");
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
function finalizeAttemptManifest(traceDirectory, patch) {
|
|
541
|
+
const path = traceManifestPath(traceDirectory);
|
|
542
|
+
const existing = readExistingManifest(traceDirectory);
|
|
543
|
+
if (!existing) {
|
|
544
|
+
logger.warn({ traceDirectory }, "[TraceBundle] Attempt manifest missing \u2014 skip finalization");
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
const finalized = {
|
|
548
|
+
...existing,
|
|
549
|
+
...patch,
|
|
550
|
+
files: {
|
|
551
|
+
...existing.files,
|
|
552
|
+
...patch.files ?? {}
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
if (patch.rawOutputs) {
|
|
556
|
+
finalized.rawOutputs = Array.from(/* @__PURE__ */ new Set([...existing.rawOutputs ?? [], ...patch.rawOutputs]));
|
|
557
|
+
}
|
|
558
|
+
writeAttemptManifest(traceDirectory, finalized);
|
|
559
|
+
}
|
|
560
|
+
function buildInitialAttemptManifest(issue, provider, role) {
|
|
561
|
+
const planVersion = issue.planVersion ?? 1;
|
|
562
|
+
const executeAttempt = issue.executeAttempt ?? 1;
|
|
563
|
+
return {
|
|
564
|
+
manifestVersion: 1,
|
|
565
|
+
issueId: issue.id,
|
|
566
|
+
issueIdentifier: issue.identifier,
|
|
567
|
+
planVersion,
|
|
568
|
+
executeAttempt,
|
|
569
|
+
provider,
|
|
570
|
+
role,
|
|
571
|
+
startedAt: now(),
|
|
572
|
+
files: {
|
|
573
|
+
attemptManifest: ATTEMPT_MANIFEST_FILE,
|
|
574
|
+
transcript: TRANSCRIPT_MIRROR_FILE,
|
|
575
|
+
turnsDir: "turns",
|
|
576
|
+
changedFiles: CHANGED_FILES_FILE,
|
|
577
|
+
diffStat: DIFF_STAT_FILE,
|
|
578
|
+
rawOutputTemplate: `outputs/execute.v${planVersion}a${executeAttempt}.t{turn}.stdout.log`
|
|
579
|
+
},
|
|
580
|
+
rawOutputs: []
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function captureWorkspaceDelta(traceDirectory, worktreePath) {
|
|
584
|
+
try {
|
|
585
|
+
const changedOutput = execSync("git diff --name-only", { cwd: worktreePath, encoding: "utf8", maxBuffer: 1e6 });
|
|
586
|
+
const changedFiles = (changedOutput || "").split("\n").map((f) => f.trim()).filter(Boolean);
|
|
587
|
+
writeFileSync(join2(traceDirectory, CHANGED_FILES_FILE), JSON.stringify(changedFiles, null, 2), "utf8");
|
|
588
|
+
const diffStat = execSync("git diff --stat", { cwd: worktreePath, encoding: "utf8", maxBuffer: 1e6 });
|
|
589
|
+
writeFileSync(join2(traceDirectory, DIFF_STAT_FILE), diffStat || "", "utf8");
|
|
590
|
+
if (changedFiles.length > 0) {
|
|
591
|
+
const diffOutput = execSync("git diff", { cwd: worktreePath, encoding: "utf8", maxBuffer: 4e6 });
|
|
592
|
+
writeFileSync(join2(traceDirectory, DIFF_PATCH_FILE), diffOutput || "", "utf8");
|
|
593
|
+
finalizeAttemptManifest(traceDirectory, { files: { diffPatch: DIFF_PATCH_FILE } });
|
|
594
|
+
} else {
|
|
595
|
+
logger.debug({ traceDirectory, issuePath: worktreePath }, "[TraceBundle] No changed files, skipping diff.patch");
|
|
596
|
+
}
|
|
597
|
+
} catch (error) {
|
|
598
|
+
logger.warn({ error: String(error), traceDirectory, worktreePath }, "[TraceBundle] Failed to capture workspace delta");
|
|
599
|
+
writeFileSync(join2(traceDirectory, CHANGED_FILES_FILE), "[]", "utf8");
|
|
600
|
+
writeFileSync(join2(traceDirectory, DIFF_STAT_FILE), "", "utf8");
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function writeTranscriptMirror(traceDirectory, transcriptEntries) {
|
|
604
|
+
try {
|
|
605
|
+
const lines = transcriptEntries.map((entry) => JSON.stringify({
|
|
606
|
+
ts: entry.ts,
|
|
607
|
+
turn: entry.turn,
|
|
608
|
+
role: entry.role,
|
|
609
|
+
model: entry.model,
|
|
610
|
+
provider: entry.provider,
|
|
611
|
+
directiveStatus: entry.directiveStatus,
|
|
612
|
+
directiveSummary: entry.directiveSummary,
|
|
613
|
+
exitCode: entry.exitCode,
|
|
614
|
+
success: entry.success,
|
|
615
|
+
tokens: entry.tokens,
|
|
616
|
+
toolsUsed: entry.toolsUsed,
|
|
617
|
+
skillsUsed: entry.skillsUsed,
|
|
618
|
+
agentsUsed: entry.agentsUsed,
|
|
619
|
+
commandsRun: entry.commandsRun,
|
|
620
|
+
durationMs: entry.durationMs,
|
|
621
|
+
contextPct: entry.contextPct,
|
|
622
|
+
outputPreview: entry.outputPreview
|
|
623
|
+
}));
|
|
624
|
+
const path = traceTranscriptPath(traceDirectory);
|
|
625
|
+
writeFileSync(path, lines.join("\n") + (lines.length > 0 ? "\n" : ""), "utf8");
|
|
626
|
+
} catch (error) {
|
|
627
|
+
logger.warn({ error: String(error), traceDirectory }, "[TraceBundle] Failed to write transcript mirror");
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
function finalizeAttemptForIssue(worktreePath, issue, patch) {
|
|
631
|
+
const planVersion = issue.planVersion ?? 1;
|
|
632
|
+
const executeAttempt = issue.executeAttempt ?? 1;
|
|
633
|
+
const dir = traceDir(worktreePath, planVersion, executeAttempt);
|
|
634
|
+
if (!existsSync2(dir)) return;
|
|
635
|
+
captureWorkspaceDelta(dir, worktreePath);
|
|
636
|
+
const transcriptEntries = readTranscript(issue.id, planVersion, executeAttempt);
|
|
637
|
+
writeTranscriptMirror(dir, transcriptEntries);
|
|
638
|
+
const railsFiles = writeRailsArtifact(dir, worktreePath, issue);
|
|
639
|
+
const similarTraceFiles = finalizeSimilarTraceSelectionArtifact(dir, worktreePath, patch);
|
|
640
|
+
const handoffFiles = writeHandoffArtifacts(dir, issue, patch, transcriptEntries);
|
|
641
|
+
finalizeAttemptManifest(dir, {
|
|
642
|
+
...patch,
|
|
643
|
+
files: {
|
|
644
|
+
...patch.files ?? {},
|
|
645
|
+
...railsFiles,
|
|
646
|
+
...similarTraceFiles,
|
|
647
|
+
...handoffFiles
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
try {
|
|
651
|
+
const contextMetrics = loadContextMetrics(dir);
|
|
652
|
+
if (contextMetrics) {
|
|
653
|
+
const crossAttemptPath = join2(dir, "cross-attempt.json");
|
|
654
|
+
let hypothesesCount = 0;
|
|
655
|
+
let pivotTriggered = false;
|
|
656
|
+
if (existsSync2(crossAttemptPath)) {
|
|
657
|
+
const ca = JSON.parse(readFileSync2(crossAttemptPath, "utf8"));
|
|
658
|
+
hypothesesCount = Array.isArray(ca?.hypotheses) ? ca.hypotheses.length : 0;
|
|
659
|
+
pivotTriggered = Boolean(ca?.strategyPivot);
|
|
660
|
+
}
|
|
661
|
+
const similarPath = join2(dir, "similar-traces.json");
|
|
662
|
+
let similarCount = 0;
|
|
663
|
+
if (existsSync2(similarPath)) {
|
|
664
|
+
const st = JSON.parse(readFileSync2(similarPath, "utf8"));
|
|
665
|
+
similarCount = Array.isArray(st?.hits) ? st.hits.length : 0;
|
|
666
|
+
}
|
|
667
|
+
writeHarnessOutcome(dir, {
|
|
668
|
+
issueId: issue.id,
|
|
669
|
+
issueIdentifier: issue.identifier,
|
|
670
|
+
planVersion,
|
|
671
|
+
executeAttempt,
|
|
672
|
+
outcome: patch.outcome ?? "unknown",
|
|
673
|
+
contextMetrics,
|
|
674
|
+
hypothesesGenerated: hypothesesCount,
|
|
675
|
+
strategyPivotTriggered: pivotTriggered,
|
|
676
|
+
similarIssuesUsed: similarCount
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
} catch {
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function persistContextMetrics(traceDirectory, metrics) {
|
|
683
|
+
try {
|
|
684
|
+
writeFileSync(join2(traceDirectory, "context-metrics.json"), JSON.stringify(metrics, null, 2), "utf8");
|
|
685
|
+
} catch (error) {
|
|
686
|
+
logger.debug({ err: String(error), traceDirectory }, "[TraceBundle] Failed to write context metrics");
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function loadContextMetrics(traceDirectory) {
|
|
690
|
+
const filePath = join2(traceDirectory, "context-metrics.json");
|
|
691
|
+
if (!existsSync2(filePath)) return null;
|
|
692
|
+
try {
|
|
693
|
+
return JSON.parse(readFileSync2(filePath, "utf8"));
|
|
694
|
+
} catch {
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
function writeHarnessOutcome(traceDirectory, outcome) {
|
|
699
|
+
try {
|
|
700
|
+
writeFileSync(join2(traceDirectory, "harness-outcome.json"), JSON.stringify(outcome, null, 2), "utf8");
|
|
701
|
+
} catch (error) {
|
|
702
|
+
logger.debug({ err: String(error), traceDirectory }, "[TraceBundle] Failed to write harness outcome");
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
function loadHarnessOutcome(traceDirectory) {
|
|
706
|
+
const filePath = join2(traceDirectory, "harness-outcome.json");
|
|
707
|
+
if (!existsSync2(filePath)) return null;
|
|
708
|
+
try {
|
|
709
|
+
return JSON.parse(readFileSync2(filePath, "utf8"));
|
|
710
|
+
} catch {
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
export {
|
|
716
|
+
recordTranscriptTurn,
|
|
717
|
+
readTranscript,
|
|
718
|
+
readAllTranscripts,
|
|
719
|
+
getTranscriptSummary,
|
|
720
|
+
TRACE_DIR,
|
|
721
|
+
traceDir,
|
|
722
|
+
readTraceContent,
|
|
723
|
+
ensureTraceDir,
|
|
724
|
+
toTraceBundle,
|
|
725
|
+
loadAttemptManifest,
|
|
726
|
+
writeAttemptManifest,
|
|
727
|
+
finalizeAttemptManifest,
|
|
728
|
+
buildInitialAttemptManifest,
|
|
729
|
+
captureWorkspaceDelta,
|
|
730
|
+
writeTranscriptMirror,
|
|
731
|
+
finalizeAttemptForIssue,
|
|
732
|
+
persistContextMetrics,
|
|
733
|
+
loadContextMetrics,
|
|
734
|
+
writeHarnessOutcome,
|
|
735
|
+
loadHarnessOutcome
|
|
736
|
+
};
|
|
737
|
+
//# sourceMappingURL=chunk-APJOZXRP.js.map
|