vibe-coding-master 0.4.26 → 0.4.28
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.
|
@@ -268,7 +268,6 @@ async function main() {
|
|
|
268
268
|
const operations = [];
|
|
269
269
|
await assertDirectory(projectRoot, "Project root");
|
|
270
270
|
const manifest = await buildManifest(projectRoot);
|
|
271
|
-
await installManifest({ projectRoot, manifest, dryRun, operations });
|
|
272
271
|
for (const definition of MANAGED_FILES) {
|
|
273
272
|
await installManagedFile({ projectRoot, definition, dryRun, operations });
|
|
274
273
|
}
|
|
@@ -284,6 +283,13 @@ async function main() {
|
|
|
284
283
|
}
|
|
285
284
|
await removeLegacyFlatSkillFiles({ projectRoot, dryRun, operations });
|
|
286
285
|
await removeLegacyCodexHarnessPaths({ projectRoot, dryRun, operations });
|
|
286
|
+
await installManifest({
|
|
287
|
+
projectRoot,
|
|
288
|
+
manifest,
|
|
289
|
+
dryRun,
|
|
290
|
+
operations,
|
|
291
|
+
forceWrite: hasRealHarnessChange(operations)
|
|
292
|
+
});
|
|
287
293
|
printReport({ projectRoot, dryRun, operations });
|
|
288
294
|
}
|
|
289
295
|
function parseArgs(argv) {
|
|
@@ -449,13 +455,17 @@ function directoryCategory(directory) {
|
|
|
449
455
|
}
|
|
450
456
|
return "harness-tool-directory";
|
|
451
457
|
}
|
|
452
|
-
async function installManifest({ projectRoot, manifest, dryRun, operations }) {
|
|
458
|
+
async function installManifest({ projectRoot, manifest, dryRun, operations, forceWrite = false }) {
|
|
453
459
|
const targetPath = path.join(projectRoot, MANIFEST_PATH);
|
|
454
460
|
const currentManifest = await readOptionalJson(targetPath);
|
|
455
461
|
if (currentManifest && manifestBodyEqual(currentManifest, manifest)) {
|
|
456
462
|
operations.push(skip(MANIFEST_PATH, "unchanged"));
|
|
457
463
|
return;
|
|
458
464
|
}
|
|
465
|
+
if (!forceWrite && currentManifest && manifestBodyEqual(currentManifest, manifest, { ignoreHarnessVersion: true })) {
|
|
466
|
+
operations.push(skip(MANIFEST_PATH, "version-only change ignored"));
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
459
469
|
await writeIfChanged({
|
|
460
470
|
targetPath,
|
|
461
471
|
relativePath: MANIFEST_PATH,
|
|
@@ -731,15 +741,23 @@ async function pathExists(absolutePath) {
|
|
|
731
741
|
throw error;
|
|
732
742
|
});
|
|
733
743
|
}
|
|
734
|
-
function manifestBodyEqual(left, right) {
|
|
744
|
+
function manifestBodyEqual(left, right, options = {}) {
|
|
735
745
|
const normalizedLeft = { ...left };
|
|
736
746
|
const normalizedRight = { ...right };
|
|
737
747
|
delete normalizedLeft.installedAt;
|
|
738
748
|
delete normalizedLeft.updatedAt;
|
|
739
749
|
delete normalizedRight.installedAt;
|
|
740
750
|
delete normalizedRight.updatedAt;
|
|
751
|
+
if (options.ignoreHarnessVersion) {
|
|
752
|
+
delete normalizedLeft.harnessVersion;
|
|
753
|
+
delete normalizedRight.harnessVersion;
|
|
754
|
+
}
|
|
741
755
|
return JSON.stringify(normalizedLeft) === JSON.stringify(normalizedRight);
|
|
742
756
|
}
|
|
757
|
+
function hasRealHarnessChange(operations) {
|
|
758
|
+
return operations.some((operation) => operation.path !== MANIFEST_PATH &&
|
|
759
|
+
(operation.status === "done" || operation.status === "plan"));
|
|
760
|
+
}
|
|
743
761
|
function resolveInside(root, relativePath) {
|
|
744
762
|
if (path.isAbsolute(relativePath)) {
|
|
745
763
|
fail(`Path must be relative: ${relativePath}`);
|
|
@@ -1284,11 +1284,7 @@ async function analyzeHarnessManifest(fs, repoRoot, vcmVersion) {
|
|
|
1284
1284
|
};
|
|
1285
1285
|
}
|
|
1286
1286
|
if (installedVersion !== vcmVersion) {
|
|
1287
|
-
return
|
|
1288
|
-
path: MANIFEST_PATH,
|
|
1289
|
-
action: "update",
|
|
1290
|
-
reason: `VCM fixed harness manifest version is ${installedVersion ?? "missing"}; current VCM version is ${vcmVersion}.`
|
|
1291
|
-
};
|
|
1287
|
+
return undefined;
|
|
1292
1288
|
}
|
|
1293
1289
|
return undefined;
|
|
1294
1290
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { VCM_ROLE_NAMES, isDispatchableRole } from "../../shared/constants.js";
|
|
4
3
|
import { VcmError } from "../errors.js";
|
|
@@ -43,10 +42,10 @@ export function createSessionService(deps) {
|
|
|
43
42
|
const permissionMode = normalizeClaudePermissionMode(input.permissionMode ?? persisted?.permissionMode);
|
|
44
43
|
const model = normalizeClaudeModel(input.model ?? persisted?.model);
|
|
45
44
|
const effort = normalizeClaudeEffort(input.effort ?? persisted?.effort);
|
|
46
|
-
const
|
|
45
|
+
const resumeClaudeSessionId = launchMode === "resume"
|
|
47
46
|
? persisted?.claudeSessionId
|
|
48
|
-
:
|
|
49
|
-
if (!
|
|
47
|
+
: undefined;
|
|
48
|
+
if (launchMode === "resume" && !resumeClaudeSessionId) {
|
|
50
49
|
throw new VcmError({
|
|
51
50
|
code: "CLAUDE_SESSION_MISSING",
|
|
52
51
|
message: `${role} does not have a session id to resume.`,
|
|
@@ -54,11 +53,14 @@ export function createSessionService(deps) {
|
|
|
54
53
|
hint: "Start the role once before using Resume."
|
|
55
54
|
});
|
|
56
55
|
}
|
|
56
|
+
const claudeSessionId = resumeClaudeSessionId ?? "";
|
|
57
57
|
const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
|
|
58
58
|
? persisted.transcriptPath
|
|
59
|
-
:
|
|
59
|
+
: resumeClaudeSessionId
|
|
60
|
+
? claudeTranscriptPath(taskRepoRoot, resumeClaudeSessionId)
|
|
61
|
+
: undefined;
|
|
60
62
|
const startCommand = {
|
|
61
|
-
...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode,
|
|
63
|
+
...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort),
|
|
62
64
|
cwd: taskRepoRoot
|
|
63
65
|
};
|
|
64
66
|
const runtimeSession = await deps.runtime.createSession({
|
|
@@ -73,7 +75,7 @@ export function createSessionService(deps) {
|
|
|
73
75
|
VCM_TASK_REPO_ROOT: taskRepoRoot,
|
|
74
76
|
VCM_TASK_SLUG: taskSlug,
|
|
75
77
|
VCM_ROLE: role,
|
|
76
|
-
VCM_SESSION_ID: claudeSessionId
|
|
78
|
+
VCM_SESSION_ID: claudeSessionId || undefined
|
|
77
79
|
},
|
|
78
80
|
cols: input.cols,
|
|
79
81
|
rows: input.rows
|
|
@@ -120,10 +122,10 @@ export function createSessionService(deps) {
|
|
|
120
122
|
const permissionMode = normalizeClaudePermissionMode(input.permissionMode ?? persisted?.permissionMode);
|
|
121
123
|
const model = normalizeClaudeModel(input.model ?? persisted?.model);
|
|
122
124
|
const effort = normalizeClaudeEffort(input.effort ?? persisted?.effort ?? "medium");
|
|
123
|
-
const
|
|
125
|
+
const resumeClaudeSessionId = launchMode === "resume"
|
|
124
126
|
? persisted?.claudeSessionId
|
|
125
|
-
:
|
|
126
|
-
if (!
|
|
127
|
+
: undefined;
|
|
128
|
+
if (launchMode === "resume" && !resumeClaudeSessionId) {
|
|
127
129
|
throw new VcmError({
|
|
128
130
|
code: "TRANSLATOR_SESSION_MISSING",
|
|
129
131
|
message: "Translator does not have a session id to resume.",
|
|
@@ -135,11 +137,14 @@ export function createSessionService(deps) {
|
|
|
135
137
|
const launchCwd = launchMode === "resume"
|
|
136
138
|
? persisted?.cwd ?? taskContext.taskRepoRoot
|
|
137
139
|
: taskContext.taskRepoRoot;
|
|
140
|
+
const claudeSessionId = resumeClaudeSessionId ?? "";
|
|
138
141
|
const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
|
|
139
142
|
? persisted.transcriptPath
|
|
140
|
-
:
|
|
143
|
+
: resumeClaudeSessionId
|
|
144
|
+
? claudeTranscriptPath(launchCwd, resumeClaudeSessionId)
|
|
145
|
+
: undefined;
|
|
141
146
|
const startCommand = {
|
|
142
|
-
...deps.claude.buildRoleStartCommand(TRANSLATOR_ROLE, config.claudeCommand, permissionMode,
|
|
147
|
+
...deps.claude.buildRoleStartCommand(TRANSLATOR_ROLE, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort),
|
|
143
148
|
cwd: launchCwd
|
|
144
149
|
};
|
|
145
150
|
const runtimeSession = await deps.runtime.createSession({
|
|
@@ -154,7 +159,7 @@ export function createSessionService(deps) {
|
|
|
154
159
|
VCM_TASK_REPO_ROOT: taskContext.taskRepoRoot,
|
|
155
160
|
VCM_TASK_SLUG: taskContext.taskSlug,
|
|
156
161
|
VCM_ROLE: TRANSLATOR_ROLE,
|
|
157
|
-
VCM_SESSION_ID: claudeSessionId
|
|
162
|
+
VCM_SESSION_ID: claudeSessionId || undefined
|
|
158
163
|
},
|
|
159
164
|
cols: input.cols,
|
|
160
165
|
rows: input.rows
|
|
@@ -197,10 +202,10 @@ export function createSessionService(deps) {
|
|
|
197
202
|
const permissionMode = normalizeClaudePermissionMode(input.permissionMode ?? persisted?.permissionMode);
|
|
198
203
|
const model = normalizeClaudeModel(input.model ?? persisted?.model);
|
|
199
204
|
const effort = normalizeClaudeEffort(input.effort ?? persisted?.effort ?? "medium");
|
|
200
|
-
const
|
|
205
|
+
const resumeClaudeSessionId = launchMode === "resume"
|
|
201
206
|
? persisted?.claudeSessionId
|
|
202
|
-
:
|
|
203
|
-
if (!
|
|
207
|
+
: undefined;
|
|
208
|
+
if (launchMode === "resume" && !resumeClaudeSessionId) {
|
|
204
209
|
throw new VcmError({
|
|
205
210
|
code: "HARNESS_ENGINEER_SESSION_MISSING",
|
|
206
211
|
message: "Harness Engineer does not have a session id to resume.",
|
|
@@ -212,11 +217,14 @@ export function createSessionService(deps) {
|
|
|
212
217
|
const launchCwd = launchMode === "resume"
|
|
213
218
|
? persisted?.cwd ?? taskContext.taskRepoRoot
|
|
214
219
|
: taskContext.taskRepoRoot;
|
|
220
|
+
const claudeSessionId = resumeClaudeSessionId ?? "";
|
|
215
221
|
const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
|
|
216
222
|
? persisted.transcriptPath
|
|
217
|
-
:
|
|
223
|
+
: resumeClaudeSessionId
|
|
224
|
+
? claudeTranscriptPath(launchCwd, resumeClaudeSessionId)
|
|
225
|
+
: undefined;
|
|
218
226
|
const startCommand = {
|
|
219
|
-
...deps.claude.buildRoleStartCommand(HARNESS_ENGINEER_ROLE, config.claudeCommand, permissionMode,
|
|
227
|
+
...deps.claude.buildRoleStartCommand(HARNESS_ENGINEER_ROLE, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort),
|
|
220
228
|
cwd: launchCwd
|
|
221
229
|
};
|
|
222
230
|
const runtimeSession = await deps.runtime.createSession({
|
|
@@ -231,7 +239,7 @@ export function createSessionService(deps) {
|
|
|
231
239
|
VCM_TASK_REPO_ROOT: taskContext.taskRepoRoot,
|
|
232
240
|
VCM_TASK_SLUG: taskContext.taskSlug,
|
|
233
241
|
VCM_ROLE: HARNESS_ENGINEER_ROLE,
|
|
234
|
-
VCM_SESSION_ID: claudeSessionId
|
|
242
|
+
VCM_SESSION_ID: claudeSessionId || undefined
|
|
235
243
|
},
|
|
236
244
|
cols: input.cols,
|
|
237
245
|
rows: input.rows
|
|
@@ -298,7 +306,9 @@ export function createSessionService(deps) {
|
|
|
298
306
|
...session,
|
|
299
307
|
cwd: targetCwd,
|
|
300
308
|
previousCwd: session.cwd,
|
|
301
|
-
transcriptPath:
|
|
309
|
+
transcriptPath: session.claudeSessionId
|
|
310
|
+
? claudeTranscriptPath(targetCwd, session.claudeSessionId)
|
|
311
|
+
: session.transcriptPath,
|
|
302
312
|
updatedAt: timestamp
|
|
303
313
|
};
|
|
304
314
|
deps.registry.upsert(normalizeProjectScopedRecordForPersistence(updated));
|
|
@@ -466,6 +476,7 @@ export function createSessionService(deps) {
|
|
|
466
476
|
await deps.runtime.stop(existing.id);
|
|
467
477
|
}
|
|
468
478
|
deps.registry.remove(existing.id);
|
|
479
|
+
await clearPersistedTranslatorSession(deps.fs, repoRoot);
|
|
469
480
|
return launchProjectTranslatorSession(repoRoot, input, "fresh");
|
|
470
481
|
},
|
|
471
482
|
async getProjectTranslatorSession(repoRoot) {
|
|
@@ -509,10 +520,11 @@ export function createSessionService(deps) {
|
|
|
509
520
|
const timestamp = now();
|
|
510
521
|
const isTurnEnd = isTurnEndHook(input.eventName);
|
|
511
522
|
const isCompact = isCompactHook(input.eventName);
|
|
523
|
+
const sessionIdentity = nextHookSessionIdentity(current, input);
|
|
512
524
|
const updated = {
|
|
513
525
|
...current,
|
|
514
|
-
claudeSessionId:
|
|
515
|
-
transcriptPath:
|
|
526
|
+
claudeSessionId: sessionIdentity.claudeSessionId,
|
|
527
|
+
transcriptPath: sessionIdentity.transcriptPath,
|
|
516
528
|
cwd: input.cwd ?? current.cwd,
|
|
517
529
|
activityStatus: isTurnEnd ? "idle" : isCompact ? current.activityStatus : "running",
|
|
518
530
|
lastHookEventAt: timestamp,
|
|
@@ -590,6 +602,7 @@ export function createSessionService(deps) {
|
|
|
590
602
|
await deps.runtime.stop(existing.id);
|
|
591
603
|
}
|
|
592
604
|
deps.registry.remove(existing.id);
|
|
605
|
+
await clearPersistedHarnessEngineerSession(deps.fs, repoRoot);
|
|
593
606
|
return launchProjectHarnessEngineerSession(repoRoot, input, "fresh");
|
|
594
607
|
},
|
|
595
608
|
async getProjectHarnessEngineerSession(repoRoot) {
|
|
@@ -633,10 +646,11 @@ export function createSessionService(deps) {
|
|
|
633
646
|
const timestamp = now();
|
|
634
647
|
const isTurnEnd = isTurnEndHook(input.eventName);
|
|
635
648
|
const isCompact = isCompactHook(input.eventName);
|
|
649
|
+
const sessionIdentity = nextHookSessionIdentity(current, input);
|
|
636
650
|
const updated = {
|
|
637
651
|
...current,
|
|
638
|
-
claudeSessionId:
|
|
639
|
-
transcriptPath:
|
|
652
|
+
claudeSessionId: sessionIdentity.claudeSessionId,
|
|
653
|
+
transcriptPath: sessionIdentity.transcriptPath,
|
|
640
654
|
cwd: input.cwd ?? current.cwd,
|
|
641
655
|
activityStatus: isTurnEnd ? "idle" : isCompact ? current.activityStatus : "running",
|
|
642
656
|
lastHookEventAt: timestamp,
|
|
@@ -725,6 +739,9 @@ export function createSessionService(deps) {
|
|
|
725
739
|
await deps.runtime.stop(existing.id);
|
|
726
740
|
}
|
|
727
741
|
deps.registry.remove(existing.id);
|
|
742
|
+
const config = await deps.projectService.loadConfig(repoRoot);
|
|
743
|
+
const task = await deps.taskService.loadTask(repoRoot, taskSlug);
|
|
744
|
+
await clearPersistedRoleSessionRecord(deps.fs, getTaskRuntimeRepoRoot(task), config.stateRoot, taskSlug, role, now());
|
|
728
745
|
return launchRoleSession(repoRoot, taskSlug, role, input, "fresh");
|
|
729
746
|
},
|
|
730
747
|
async getRoleSession(repoRoot, taskSlug, role) {
|
|
@@ -800,10 +817,11 @@ export function createSessionService(deps) {
|
|
|
800
817
|
const timestamp = now();
|
|
801
818
|
const isTurnEnd = isTurnEndHook(input.eventName);
|
|
802
819
|
const isCompact = isCompactHook(input.eventName);
|
|
820
|
+
const sessionIdentity = nextHookSessionIdentity(current, input);
|
|
803
821
|
const updated = {
|
|
804
822
|
...current,
|
|
805
|
-
claudeSessionId:
|
|
806
|
-
transcriptPath:
|
|
823
|
+
claudeSessionId: sessionIdentity.claudeSessionId,
|
|
824
|
+
transcriptPath: sessionIdentity.transcriptPath,
|
|
807
825
|
cwd: input.cwd ?? current.cwd,
|
|
808
826
|
activityStatus: isTurnEnd ? "idle" : isCompact ? current.activityStatus : "running",
|
|
809
827
|
lastHookEventAt: timestamp,
|
|
@@ -890,6 +908,9 @@ function toRoleSessionRecordView(record, runtime) {
|
|
|
890
908
|
};
|
|
891
909
|
}
|
|
892
910
|
function matchesRoleHookSession(record, input) {
|
|
911
|
+
if (!record.claudeSessionId && !record.transcriptPath) {
|
|
912
|
+
return input.eventName === "UserPromptSubmit";
|
|
913
|
+
}
|
|
893
914
|
if (input.sessionId && record.claudeSessionId === input.sessionId) {
|
|
894
915
|
return true;
|
|
895
916
|
}
|
|
@@ -901,6 +922,20 @@ function matchesRoleHookSession(record, input) {
|
|
|
901
922
|
}
|
|
902
923
|
return false;
|
|
903
924
|
}
|
|
925
|
+
function nextHookSessionIdentity(current, input) {
|
|
926
|
+
const canRecordFirstClaudeSessionId = Boolean(!current.claudeSessionId &&
|
|
927
|
+
input.eventName === "UserPromptSubmit" &&
|
|
928
|
+
input.sessionId);
|
|
929
|
+
const hasConfirmedClaudeSessionId = Boolean(current.claudeSessionId || canRecordFirstClaudeSessionId);
|
|
930
|
+
return {
|
|
931
|
+
claudeSessionId: canRecordFirstClaudeSessionId
|
|
932
|
+
? input.sessionId ?? current.claudeSessionId
|
|
933
|
+
: current.claudeSessionId,
|
|
934
|
+
transcriptPath: hasConfirmedClaudeSessionId
|
|
935
|
+
? input.transcriptPath ?? current.transcriptPath
|
|
936
|
+
: current.transcriptPath
|
|
937
|
+
};
|
|
938
|
+
}
|
|
904
939
|
function isTurnEndHook(eventName) {
|
|
905
940
|
return eventName === "Stop" || eventName === "StopFailure";
|
|
906
941
|
}
|
|
@@ -1068,6 +1103,9 @@ function buildHarnessRefreshPrompt(role) {
|
|
|
1068
1103
|
].join("\n");
|
|
1069
1104
|
}
|
|
1070
1105
|
async function persistTaskSession(fs, repoRoot, stateRoot, session) {
|
|
1106
|
+
if (!hasConfirmedClaudeSessionId(session)) {
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1071
1109
|
const sessionPath = getTaskSessionPath(repoRoot, stateRoot, session.taskSlug);
|
|
1072
1110
|
const empty = createEmptyTaskSessionRecord(session.taskSlug, session.updatedAt);
|
|
1073
1111
|
const current = await fs.pathExists(sessionPath)
|
|
@@ -1092,6 +1130,23 @@ async function persistTaskSession(fs, repoRoot, stateRoot, session) {
|
|
|
1092
1130
|
}
|
|
1093
1131
|
});
|
|
1094
1132
|
}
|
|
1133
|
+
async function clearPersistedRoleSessionRecord(fs, repoRoot, stateRoot, taskSlug, role, updatedAt) {
|
|
1134
|
+
const sessionPath = getTaskSessionPath(repoRoot, stateRoot, taskSlug);
|
|
1135
|
+
const current = await fs.pathExists(sessionPath)
|
|
1136
|
+
? await fs.readJson(sessionPath)
|
|
1137
|
+
: createEmptyTaskSessionRecord(taskSlug, updatedAt);
|
|
1138
|
+
await fs.writeJsonAtomic(sessionPath, {
|
|
1139
|
+
...current,
|
|
1140
|
+
updatedAt,
|
|
1141
|
+
roles: {
|
|
1142
|
+
...current.roles,
|
|
1143
|
+
[role]: {
|
|
1144
|
+
id: null,
|
|
1145
|
+
status: "not_started"
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1095
1150
|
async function persistRoleSessionRecord(fs, baseRepoRoot, taskRepoRoot, stateRoot, session) {
|
|
1096
1151
|
if (session.role === TRANSLATOR_ROLE) {
|
|
1097
1152
|
await persistTranslatorSession(fs, baseRepoRoot, session);
|
|
@@ -1104,6 +1159,9 @@ async function persistRoleSessionRecord(fs, baseRepoRoot, taskRepoRoot, stateRoo
|
|
|
1104
1159
|
await persistTaskSession(fs, taskRepoRoot, stateRoot, session);
|
|
1105
1160
|
}
|
|
1106
1161
|
async function persistTranslatorSession(fs, repoRoot, session) {
|
|
1162
|
+
if (!hasConfirmedClaudeSessionId(session)) {
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1107
1165
|
await fs.writeJsonAtomic(resolveRepoPath(repoRoot, TRANSLATOR_SESSION_PATH), {
|
|
1108
1166
|
version: 1,
|
|
1109
1167
|
role: session.role,
|
|
@@ -1114,7 +1172,13 @@ async function persistTranslatorSession(fs, repoRoot, session) {
|
|
|
1114
1172
|
}
|
|
1115
1173
|
});
|
|
1116
1174
|
}
|
|
1175
|
+
async function clearPersistedTranslatorSession(fs, repoRoot) {
|
|
1176
|
+
await removePersistedProjectSessionFile(fs, repoRoot, TRANSLATOR_SESSION_PATH);
|
|
1177
|
+
}
|
|
1117
1178
|
async function persistHarnessEngineerSession(fs, repoRoot, session) {
|
|
1179
|
+
if (!hasConfirmedClaudeSessionId(session)) {
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1118
1182
|
await fs.writeJsonAtomic(resolveRepoPath(repoRoot, HARNESS_ENGINEER_SESSION_PATH), {
|
|
1119
1183
|
version: 1,
|
|
1120
1184
|
role: session.role,
|
|
@@ -1125,9 +1189,30 @@ async function persistHarnessEngineerSession(fs, repoRoot, session) {
|
|
|
1125
1189
|
}
|
|
1126
1190
|
});
|
|
1127
1191
|
}
|
|
1192
|
+
async function clearPersistedHarnessEngineerSession(fs, repoRoot) {
|
|
1193
|
+
await removePersistedProjectSessionFile(fs, repoRoot, HARNESS_ENGINEER_SESSION_PATH);
|
|
1194
|
+
}
|
|
1195
|
+
async function removePersistedProjectSessionFile(fs, repoRoot, relativePath) {
|
|
1196
|
+
const sessionPath = resolveRepoPath(repoRoot, relativePath);
|
|
1197
|
+
if (!(await fs.pathExists(sessionPath))) {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
if (!fs.removePath) {
|
|
1201
|
+
throw new VcmError({
|
|
1202
|
+
code: "SESSION_CLEAR_UNAVAILABLE",
|
|
1203
|
+
message: "VCM cannot clear the persisted Claude session file in this runtime.",
|
|
1204
|
+
statusCode: 500,
|
|
1205
|
+
hint: `Remove ${relativePath} manually before restarting the role.`
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
await fs.removePath(sessionPath, { force: true });
|
|
1209
|
+
}
|
|
1128
1210
|
function isProjectRoleSessionFile(value) {
|
|
1129
1211
|
return "record" in value && typeof value.record === "object" && value.record !== null;
|
|
1130
1212
|
}
|
|
1213
|
+
function hasConfirmedClaudeSessionId(session) {
|
|
1214
|
+
return session.claudeSessionId.trim().length > 0;
|
|
1215
|
+
}
|
|
1131
1216
|
function createEmptyTaskSessionRecord(taskSlug, updatedAt) {
|
|
1132
1217
|
return {
|
|
1133
1218
|
version: 1,
|