vibe-coding-master 0.8.2 → 0.8.3
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 +3 -8
- package/dist/backend/api/task-routes.js +2 -31
- package/dist/backend/cli/install-vcm-harness.js +0 -7
- package/dist/backend/server.js +0 -9
- package/dist/backend/services/claude-hook-service.js +0 -89
- package/dist/backend/services/harness-service.js +0 -7
- package/dist/backend/services/runtime-coordinator-service.js +0 -10
- package/dist/backend/services/session-service.js +3 -70
- package/dist-frontend/assets/{index-BDUbu5St.js → index-C21rIHsU.js} +39 -39
- package/dist-frontend/assets/{index-DDmygnV6.css → index-Dc0_KSf4.css} +1 -1
- package/dist-frontend/index.html +2 -2
- package/package.json +1 -1
- package/dist/backend/services/role-stall-detector-service.js +0 -322
- package/dist/shared/types/role-stall.js +0 -1
package/README.md
CHANGED
|
@@ -506,14 +506,9 @@ provide the language server for each detected language: `rust-analyzer`,
|
|
|
506
506
|
relationships. Accessors, backing fields, trait items, implementations, wrappers,
|
|
507
507
|
and aliases are queried as separate symbols; a bounded source-text fallback may
|
|
508
508
|
only locate candidates after the correct LSP query is demonstrably partial.
|
|
509
|
-
Other roles use generated context and ordinary source reads.
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
real workspace indexing and semantic query retries. VCM uses Claude Code progress
|
|
513
|
-
hooks to detect a model response, tool call, subagent, or compaction that may be
|
|
514
|
-
stalled. Detection only opens a warning; it does not interrupt the Session or
|
|
515
|
-
change the Round. The user can ignore that warning or explicitly stop and resume
|
|
516
|
-
the same Claude Session from the warning dialog.
|
|
509
|
+
Other roles use generated context and ordinary source reads. Harness Studio
|
|
510
|
+
reports whether the server executable and plugin can run; the role Session
|
|
511
|
+
performs the real workspace indexing and semantic query retries.
|
|
517
512
|
|
|
518
513
|
Harness Engineer is task-scoped and runs from the active task worktree. The
|
|
519
514
|
backend automatically starts a fresh Harness Engineer for each active task or
|
|
@@ -77,8 +77,7 @@ export function registerTaskRoutes(app, deps) {
|
|
|
77
77
|
orchestration,
|
|
78
78
|
roundState: displayedRoundState,
|
|
79
79
|
workflowState,
|
|
80
|
-
architectRestart: deps.architectRestartService.getState(project.repoRoot, taskSlug)
|
|
81
|
-
roleStallWarning: deps.roleStallDetector.getWarning(project.repoRoot, taskSlug)
|
|
80
|
+
architectRestart: deps.architectRestartService.getState(project.repoRoot, taskSlug)
|
|
82
81
|
};
|
|
83
82
|
}
|
|
84
83
|
catch (error) {
|
|
@@ -93,30 +92,12 @@ export function registerTaskRoutes(app, deps) {
|
|
|
93
92
|
},
|
|
94
93
|
roundState: degradedRoundState(taskSlug),
|
|
95
94
|
workflowState: degradedWorkflowState(taskSlug),
|
|
96
|
-
architectRestart: deps.architectRestartService.getState(repoRoot, taskSlug)
|
|
97
|
-
roleStallWarning: deps.roleStallDetector.getWarning(repoRoot, taskSlug)
|
|
95
|
+
architectRestart: deps.architectRestartService.getState(repoRoot, taskSlug)
|
|
98
96
|
};
|
|
99
97
|
}
|
|
100
98
|
throw error;
|
|
101
99
|
}
|
|
102
100
|
});
|
|
103
|
-
app.post("/api/tasks/:taskSlug/role-stall/ignore", async (request) => {
|
|
104
|
-
const project = await requireCurrentProject(deps.projectService);
|
|
105
|
-
return deps.roleStallDetector.ignoreWarning(project.repoRoot, request.params.taskSlug, requireWarningId(request.body?.warningId));
|
|
106
|
-
});
|
|
107
|
-
app.post("/api/tasks/:taskSlug/role-stall/recover", async (request) => {
|
|
108
|
-
const project = await requireCurrentProject(deps.projectService);
|
|
109
|
-
const config = await deps.projectService.loadConfig(project.repoRoot);
|
|
110
|
-
const task = await deps.taskService.loadTask(project.repoRoot, request.params.taskSlug);
|
|
111
|
-
const taskRepoRoot = getTaskRuntimeRepoRoot(task);
|
|
112
|
-
return deps.roleStallDetector.recoverWarning({
|
|
113
|
-
repoRoot: project.repoRoot,
|
|
114
|
-
taskRepoRoot,
|
|
115
|
-
stateRoot: config.stateRoot,
|
|
116
|
-
taskSlug: task.taskSlug,
|
|
117
|
-
warningId: requireWarningId(request.body?.warningId)
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
101
|
app.post("/api/tasks/:taskSlug/workflow-state", async (request) => {
|
|
121
102
|
const project = await requireCurrentProject(deps.projectService);
|
|
122
103
|
const config = await deps.projectService.loadConfig(project.repoRoot);
|
|
@@ -155,16 +136,6 @@ async function delayFlowPauseForTranslation(deps, input) {
|
|
|
155
136
|
return input.roundState;
|
|
156
137
|
}
|
|
157
138
|
}
|
|
158
|
-
function requireWarningId(value) {
|
|
159
|
-
if (typeof value === "string" && value.trim()) {
|
|
160
|
-
return value.trim();
|
|
161
|
-
}
|
|
162
|
-
throw new VcmError({
|
|
163
|
-
code: "ROLE_STALL_WARNING_ID_REQUIRED",
|
|
164
|
-
message: "A role stall warning id is required.",
|
|
165
|
-
statusCode: 400
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
139
|
async function requireCurrentProject(projectService) {
|
|
169
140
|
const project = await projectService.getCurrentProject();
|
|
170
141
|
if (!project) {
|
|
@@ -63,15 +63,8 @@ const VCM_HOOK_DEFINITIONS = [
|
|
|
63
63
|
{ eventName: "PreToolUse", matcher: "Write|Edit", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
|
|
64
64
|
{ eventName: "PreToolUse", matcher: "Agent", command: VCM_SUBAGENT_GUARD_HOOK_COMMAND, timeout: 10 },
|
|
65
65
|
{ eventName: "UserPromptSubmit", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
66
|
-
{ eventName: "PreToolUse", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
67
|
-
{ eventName: "PostToolUse", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
68
|
-
{ eventName: "PostToolUseFailure", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
69
|
-
{ eventName: "PostToolBatch", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
70
|
-
{ eventName: "SubagentStart", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
71
|
-
{ eventName: "SubagentStop", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
72
66
|
{ eventName: "Stop", command: VCM_STOP_HOOK_COMMAND, timeout: 10 },
|
|
73
67
|
{ eventName: "StopFailure", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
74
|
-
{ eventName: "PreCompact", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
75
68
|
{ eventName: "PostCompact", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
76
69
|
{ eventName: "PermissionRequest", command: VCM_PERMISSION_REQUEST_HOOK_COMMAND, timeout: 5 }
|
|
77
70
|
];
|
package/dist/backend/server.js
CHANGED
|
@@ -14,7 +14,6 @@ import { createCodexBridgeIntegrationService } from "./services/codex-bridge-int
|
|
|
14
14
|
import { createAutoMemoryService } from "./services/auto-memory-service.js";
|
|
15
15
|
import { createArchitectRestartService } from "./services/architect-restart-service.js";
|
|
16
16
|
import { createRoleContextRestartService } from "./services/role-context-restart-service.js";
|
|
17
|
-
import { createRoleStallDetectorService } from "./services/role-stall-detector-service.js";
|
|
18
17
|
import { createClaudeTranscriptService } from "./services/claude-transcript-service.js";
|
|
19
18
|
import { createGateReviewService } from "./services/gate-review-service.js";
|
|
20
19
|
import { createHarnessFeedbackService } from "./services/harness-feedback-service.js";
|
|
@@ -137,7 +136,6 @@ export async function createServer(deps, options = {}) {
|
|
|
137
136
|
roundService: deps.roundService,
|
|
138
137
|
taskWorkflowService: deps.taskWorkflowService,
|
|
139
138
|
architectRestartService: deps.architectRestartService,
|
|
140
|
-
roleStallDetector: deps.roleStallDetector,
|
|
141
139
|
translationService: deps.translationService
|
|
142
140
|
});
|
|
143
141
|
registerSessionRoutes(app, {
|
|
@@ -351,10 +349,6 @@ export function createDefaultServerDeps(options = {}) {
|
|
|
351
349
|
sessionService
|
|
352
350
|
});
|
|
353
351
|
const transcripts = createClaudeTranscriptService();
|
|
354
|
-
const roleStallDetector = createRoleStallDetectorService({
|
|
355
|
-
sessionService,
|
|
356
|
-
roundService
|
|
357
|
-
});
|
|
358
352
|
const translationService = createTranslationService({
|
|
359
353
|
runtime,
|
|
360
354
|
sessionRegistry: registry,
|
|
@@ -432,7 +426,6 @@ export function createDefaultServerDeps(options = {}) {
|
|
|
432
426
|
translationWorkerService,
|
|
433
427
|
architectRestartService,
|
|
434
428
|
roleContextRestartService,
|
|
435
|
-
roleStallDetector,
|
|
436
429
|
workflowControlService
|
|
437
430
|
});
|
|
438
431
|
const runtimeCoordinator = createRuntimeCoordinatorService({
|
|
@@ -445,7 +438,6 @@ export function createDefaultServerDeps(options = {}) {
|
|
|
445
438
|
harnessFeedbackService,
|
|
446
439
|
autoMemoryService,
|
|
447
440
|
roundService,
|
|
448
|
-
roleStallDetector,
|
|
449
441
|
gatewayService,
|
|
450
442
|
async getStateRoot(repoRoot) {
|
|
451
443
|
return (await projectService.loadConfig(repoRoot)).stateRoot;
|
|
@@ -487,7 +479,6 @@ export function createDefaultServerDeps(options = {}) {
|
|
|
487
479
|
autoMemoryService,
|
|
488
480
|
commandDispatcher,
|
|
489
481
|
claudeHookService,
|
|
490
|
-
roleStallDetector,
|
|
491
482
|
messageService,
|
|
492
483
|
taskLaunchService,
|
|
493
484
|
gateReviewService,
|
|
@@ -257,12 +257,6 @@ export function createClaudeHookService(deps) {
|
|
|
257
257
|
role: input.role,
|
|
258
258
|
eventName
|
|
259
259
|
});
|
|
260
|
-
await deps.roleStallDetector?.recordHook({
|
|
261
|
-
...createStallContext(context),
|
|
262
|
-
role: input.role,
|
|
263
|
-
eventName,
|
|
264
|
-
event: input.event
|
|
265
|
-
});
|
|
266
260
|
}
|
|
267
261
|
if (session) {
|
|
268
262
|
await deps.translationService.recordConversationBoundary({
|
|
@@ -360,12 +354,6 @@ export function createClaudeHookService(deps) {
|
|
|
360
354
|
}
|
|
361
355
|
}
|
|
362
356
|
}
|
|
363
|
-
await deps.roleStallDetector?.recordHook({
|
|
364
|
-
...createStallContext(context),
|
|
365
|
-
role: input.role,
|
|
366
|
-
eventName,
|
|
367
|
-
event: input.event
|
|
368
|
-
});
|
|
369
357
|
if (pmAwaitingUser) {
|
|
370
358
|
return recordTurnEnd(input, context, eventName, {
|
|
371
359
|
dispatchRouteFiles: false,
|
|
@@ -417,12 +405,6 @@ export function createClaudeHookService(deps) {
|
|
|
417
405
|
if (memoryResult) {
|
|
418
406
|
return memoryResult;
|
|
419
407
|
}
|
|
420
|
-
await deps.roleStallDetector?.recordHook({
|
|
421
|
-
...createStallContext(context),
|
|
422
|
-
role: input.role,
|
|
423
|
-
eventName,
|
|
424
|
-
event: input.event
|
|
425
|
-
});
|
|
426
408
|
const routeDispatchInput = createRouteDispatchInput(input, context);
|
|
427
409
|
const pending = await deps.messageService.listPendingRouteFiles(routeDispatchInput);
|
|
428
410
|
const hasCompletionEvidence = pending.some((routeFile) => routeFile.fromRole === input.role);
|
|
@@ -496,14 +478,6 @@ export function createClaudeHookService(deps) {
|
|
|
496
478
|
runtimeSessionId: stringOrUndefined(input.event.vcm_runtime_session_id),
|
|
497
479
|
runtimeSessionToken: input.runtimeSessionToken
|
|
498
480
|
});
|
|
499
|
-
if (session) {
|
|
500
|
-
await deps.roleStallDetector?.recordHook({
|
|
501
|
-
...createStallContext(context),
|
|
502
|
-
role: input.role,
|
|
503
|
-
eventName,
|
|
504
|
-
event: input.event
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
481
|
return {
|
|
508
482
|
ok: true,
|
|
509
483
|
eventName,
|
|
@@ -637,14 +611,6 @@ export function createClaudeHookService(deps) {
|
|
|
637
611
|
eventName
|
|
638
612
|
});
|
|
639
613
|
}
|
|
640
|
-
if (boundToTask) {
|
|
641
|
-
await deps.roleStallDetector?.recordHook({
|
|
642
|
-
...createStallContext(context),
|
|
643
|
-
role: input.role,
|
|
644
|
-
eventName,
|
|
645
|
-
event: input.event
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
614
|
await deps.autoMemoryService.handleRoleHook({
|
|
649
615
|
baseRepoRoot: context.project.repoRoot,
|
|
650
616
|
taskRepoRoot: context.taskRepoRoot,
|
|
@@ -869,31 +835,6 @@ export function createClaudeHookService(deps) {
|
|
|
869
835
|
taskSlug: context.taskSlug
|
|
870
836
|
};
|
|
871
837
|
}
|
|
872
|
-
function createStallContext(context) {
|
|
873
|
-
return {
|
|
874
|
-
repoRoot: context.project.repoRoot,
|
|
875
|
-
taskRepoRoot: context.taskRepoRoot,
|
|
876
|
-
stateRoot: context.config.stateRoot,
|
|
877
|
-
taskSlug: context.taskSlug
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
async function processProgressHook(input, eventName) {
|
|
881
|
-
const role = input.role;
|
|
882
|
-
if (!isVcmRoleName(role)) {
|
|
883
|
-
return completedHookResult(input, eventName);
|
|
884
|
-
}
|
|
885
|
-
const context = await getHookContext(input);
|
|
886
|
-
if (!(await isCurrentRoleHook(context, input, eventName))) {
|
|
887
|
-
return completedHookResult(input, eventName);
|
|
888
|
-
}
|
|
889
|
-
await deps.roleStallDetector?.recordHook({
|
|
890
|
-
...createStallContext(context),
|
|
891
|
-
role,
|
|
892
|
-
eventName,
|
|
893
|
-
event: input.event
|
|
894
|
-
});
|
|
895
|
-
return completedHookResult(input, eventName);
|
|
896
|
-
}
|
|
897
838
|
function renderStopFailureRecoveryPrompt() {
|
|
898
839
|
return [
|
|
899
840
|
"[VCM Recovery]",
|
|
@@ -936,17 +877,6 @@ export function createClaudeHookService(deps) {
|
|
|
936
877
|
});
|
|
937
878
|
}
|
|
938
879
|
const preferences = await deps.appSettings.getPreferences();
|
|
939
|
-
if (deps.roleStallDetector) {
|
|
940
|
-
const context = await getHookContext(input);
|
|
941
|
-
if (await isCurrentRoleHook(context, input, "PermissionRequest")) {
|
|
942
|
-
await deps.roleStallDetector.recordHook({
|
|
943
|
-
...createStallContext(context),
|
|
944
|
-
role: input.role,
|
|
945
|
-
eventName: "PermissionRequest",
|
|
946
|
-
event: input.event
|
|
947
|
-
});
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
880
|
if (preferences.permissionRequestMode !== "allowAll") {
|
|
951
881
|
return undefined;
|
|
952
882
|
}
|
|
@@ -963,9 +893,6 @@ export function createClaudeHookService(deps) {
|
|
|
963
893
|
async handleHook(input) {
|
|
964
894
|
return withRoleHookLock(input, async () => {
|
|
965
895
|
const eventName = parseHookEvent(input.event.hook_event_name);
|
|
966
|
-
if (isProgressHook(eventName)) {
|
|
967
|
-
return processProgressHook(input, eventName);
|
|
968
|
-
}
|
|
969
896
|
if (isTranslatorToolRoleName(input.role)) {
|
|
970
897
|
return processTranslatorHook(input);
|
|
971
898
|
}
|
|
@@ -1004,13 +931,6 @@ function parseHookEvent(value) {
|
|
|
1004
931
|
if (value === "UserPromptSubmit"
|
|
1005
932
|
|| value === "Stop"
|
|
1006
933
|
|| value === "StopFailure"
|
|
1007
|
-
|| value === "PreToolUse"
|
|
1008
|
-
|| value === "PostToolUse"
|
|
1009
|
-
|| value === "PostToolUseFailure"
|
|
1010
|
-
|| value === "PostToolBatch"
|
|
1011
|
-
|| value === "SubagentStart"
|
|
1012
|
-
|| value === "SubagentStop"
|
|
1013
|
-
|| value === "PreCompact"
|
|
1014
934
|
|| value === "PostCompact") {
|
|
1015
935
|
return value;
|
|
1016
936
|
}
|
|
@@ -1021,15 +941,6 @@ function parseHookEvent(value) {
|
|
|
1021
941
|
hint: "Use a Claude Code event installed by the VCM Harness."
|
|
1022
942
|
});
|
|
1023
943
|
}
|
|
1024
|
-
function isProgressHook(eventName) {
|
|
1025
|
-
return eventName === "PreToolUse"
|
|
1026
|
-
|| eventName === "PostToolUse"
|
|
1027
|
-
|| eventName === "PostToolUseFailure"
|
|
1028
|
-
|| eventName === "PostToolBatch"
|
|
1029
|
-
|| eventName === "SubagentStart"
|
|
1030
|
-
|| eventName === "SubagentStop"
|
|
1031
|
-
|| eventName === "PreCompact";
|
|
1032
|
-
}
|
|
1033
944
|
function parseBusinessHookEvent(value) {
|
|
1034
945
|
const eventName = parseHookEvent(value);
|
|
1035
946
|
if (eventName === "UserPromptSubmit"
|
|
@@ -63,15 +63,8 @@ const VCM_HOOK_DEFINITIONS = [
|
|
|
63
63
|
{ eventName: "PreToolUse", matcher: "Write|Edit", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
|
|
64
64
|
{ eventName: "PreToolUse", matcher: "Agent", command: VCM_SUBAGENT_GUARD_HOOK_COMMAND, timeout: 10 },
|
|
65
65
|
{ eventName: "UserPromptSubmit", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
66
|
-
{ eventName: "PreToolUse", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
67
|
-
{ eventName: "PostToolUse", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
68
|
-
{ eventName: "PostToolUseFailure", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
69
|
-
{ eventName: "PostToolBatch", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
70
|
-
{ eventName: "SubagentStart", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
71
|
-
{ eventName: "SubagentStop", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
72
66
|
{ eventName: "Stop", command: VCM_STOP_HOOK_COMMAND, timeout: 10 },
|
|
73
67
|
{ eventName: "StopFailure", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
74
|
-
{ eventName: "PreCompact", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
75
68
|
{ eventName: "PostCompact", command: VCM_HOOK_COMMAND, timeout: 5 },
|
|
76
69
|
{ eventName: "PermissionRequest", command: VCM_PERMISSION_REQUEST_HOOK_COMMAND, timeout: 5 }
|
|
77
70
|
];
|
|
@@ -42,17 +42,9 @@ export function createRuntimeCoordinatorService(deps) {
|
|
|
42
42
|
]);
|
|
43
43
|
const preferences = await deps.appSettings.getPreferences();
|
|
44
44
|
if (!activeTask) {
|
|
45
|
-
deps.roleStallDetector.clearProject(repoRoot);
|
|
46
45
|
return { activeTask: null, gatewayStatus };
|
|
47
46
|
}
|
|
48
47
|
const taskRepoRoot = getTaskRuntimeRepoRoot(activeTask);
|
|
49
|
-
const stateRoot = await deps.getStateRoot(repoRoot);
|
|
50
|
-
await deps.roleStallDetector.reconcileTask({
|
|
51
|
-
repoRoot,
|
|
52
|
-
taskRepoRoot,
|
|
53
|
-
stateRoot,
|
|
54
|
-
taskSlug: activeTask.taskSlug
|
|
55
|
-
});
|
|
56
48
|
const harnessInitialized = await deps.harnessService.getHarnessStatus(taskRepoRoot)
|
|
57
49
|
.then((status) => status.initialized)
|
|
58
50
|
.catch(() => false);
|
|
@@ -86,7 +78,6 @@ export function createRuntimeCoordinatorService(deps) {
|
|
|
86
78
|
void reconcileCurrentProject().catch(() => undefined);
|
|
87
79
|
},
|
|
88
80
|
stop() {
|
|
89
|
-
deps.roleStallDetector.stop();
|
|
90
81
|
if (reconcileTimer === undefined) {
|
|
91
82
|
return;
|
|
92
83
|
}
|
|
@@ -98,7 +89,6 @@ export function createRuntimeCoordinatorService(deps) {
|
|
|
98
89
|
async function reconcileCurrentProject() {
|
|
99
90
|
const project = await deps.projectService.getCurrentProject();
|
|
100
91
|
if (!project) {
|
|
101
|
-
deps.roleStallDetector.stop();
|
|
102
92
|
return;
|
|
103
93
|
}
|
|
104
94
|
await reconcileProject(project.repoRoot);
|
|
@@ -721,8 +721,7 @@ export function createSessionService(deps) {
|
|
|
721
721
|
return undefined;
|
|
722
722
|
}
|
|
723
723
|
const timestamp = now();
|
|
724
|
-
const turnWasRunning = current.activityStatus === "running"
|
|
725
|
-
&& current.expectedRuntimeExitReason !== "role-stall-recovery";
|
|
724
|
+
const turnWasRunning = current.activityStatus === "running";
|
|
726
725
|
const updated = {
|
|
727
726
|
...current,
|
|
728
727
|
status: input.status,
|
|
@@ -738,71 +737,6 @@ export function createSessionService(deps) {
|
|
|
738
737
|
await persistRoleSessionRecord(deps.fs, repoRoot, getTaskRuntimeRepoRoot(task), config.stateRoot, updated);
|
|
739
738
|
return { record: updated, turnWasRunning };
|
|
740
739
|
}
|
|
741
|
-
async function persistTaskRoleRecord(repoRoot, taskSlug, record) {
|
|
742
|
-
deps.registry.upsert(record);
|
|
743
|
-
const config = await deps.projectService.loadConfig(repoRoot);
|
|
744
|
-
const task = await deps.taskService.loadTask(repoRoot, taskSlug);
|
|
745
|
-
await persistRoleSessionRecord(deps.fs, repoRoot, getTaskRuntimeRepoRoot(task), config.stateRoot, record);
|
|
746
|
-
return record;
|
|
747
|
-
}
|
|
748
|
-
async function recoverRoleSession(repoRoot, taskSlug, input) {
|
|
749
|
-
const current = await getTaskRoleSessionView(repoRoot, taskSlug, input.role);
|
|
750
|
-
if (!current
|
|
751
|
-
|| current.id !== input.expectedSessionId
|
|
752
|
-
|| current.runtimeSessionToken !== input.expectedRuntimeSessionToken
|
|
753
|
-
|| current.status !== "running"
|
|
754
|
-
|| current.activityStatus !== "running") {
|
|
755
|
-
throw new VcmError({
|
|
756
|
-
code: "ROLE_STALL_SESSION_CHANGED",
|
|
757
|
-
message: `${input.role} session changed before recovery could begin.`,
|
|
758
|
-
statusCode: 409
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
if (!current.claudeSessionId) {
|
|
762
|
-
throw new VcmError({
|
|
763
|
-
code: "CLAUDE_SESSION_MISSING",
|
|
764
|
-
message: `${input.role} does not have a confirmed Claude session id to resume.`,
|
|
765
|
-
statusCode: 409
|
|
766
|
-
});
|
|
767
|
-
}
|
|
768
|
-
const prepared = await persistTaskRoleRecord(repoRoot, taskSlug, {
|
|
769
|
-
...current,
|
|
770
|
-
expectedRuntimeExitReason: "role-stall-recovery",
|
|
771
|
-
updatedAt: now()
|
|
772
|
-
});
|
|
773
|
-
if (deps.runtime.getSession(prepared.id)) {
|
|
774
|
-
await deps.runtime.stop(prepared.id);
|
|
775
|
-
}
|
|
776
|
-
await persistTaskRoleRecord(repoRoot, taskSlug, {
|
|
777
|
-
...prepared,
|
|
778
|
-
status: "exited",
|
|
779
|
-
activityStatus: "idle",
|
|
780
|
-
pid: undefined,
|
|
781
|
-
expectedRuntimeExitReason: undefined,
|
|
782
|
-
updatedAt: now()
|
|
783
|
-
});
|
|
784
|
-
const resumed = await launchRoleSession(repoRoot, taskSlug, input.role, {
|
|
785
|
-
permissionMode: prepared.permissionMode,
|
|
786
|
-
model: prepared.model,
|
|
787
|
-
effort: prepared.effort
|
|
788
|
-
}, "resume");
|
|
789
|
-
if ((await waitForSessionInputReady(resumed.id)) === "exited") {
|
|
790
|
-
throw new VcmError({
|
|
791
|
-
code: "ROLE_STALL_RESUME_FAILED",
|
|
792
|
-
message: `${input.role} session exited before recovery input could be submitted.`,
|
|
793
|
-
statusCode: 409
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
await submitTerminalInput(deps.runtime, resumed.id, input.recoveryPrompt);
|
|
797
|
-
return persistTaskRoleRecord(repoRoot, taskSlug, {
|
|
798
|
-
...resumed,
|
|
799
|
-
activityStatus: "running",
|
|
800
|
-
lastTurnStartedAt: now(),
|
|
801
|
-
lastHookEventAt: now(),
|
|
802
|
-
expectedRuntimeExitReason: undefined,
|
|
803
|
-
updatedAt: now()
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
740
|
return {
|
|
807
741
|
async assertModelLaunchReady(model = "default") {
|
|
808
742
|
await getModelLaunchEnvironment(normalizeClaudeModel(model));
|
|
@@ -1307,8 +1241,7 @@ export function createSessionService(deps) {
|
|
|
1307
1241
|
: undefined;
|
|
1308
1242
|
}
|
|
1309
1243
|
return markTaskRoleActivityIdle(repoRoot, taskSlug, role);
|
|
1310
|
-
}
|
|
1311
|
-
recoverRoleSession
|
|
1244
|
+
}
|
|
1312
1245
|
};
|
|
1313
1246
|
}
|
|
1314
1247
|
function toRoleSessionRecordView(record, runtime) {
|
|
@@ -1398,7 +1331,7 @@ function isSessionMissingError(error) {
|
|
|
1398
1331
|
error.code === "SESSION_MISSING";
|
|
1399
1332
|
}
|
|
1400
1333
|
function withoutRuntimeOnlySessionFields(session) {
|
|
1401
|
-
const { pid: _pid, runtimeSessionToken: _runtimeSessionToken,
|
|
1334
|
+
const { pid: _pid, runtimeSessionToken: _runtimeSessionToken, ...persisted } = session;
|
|
1402
1335
|
return persisted;
|
|
1403
1336
|
}
|
|
1404
1337
|
function defaultIsProcessAlive(pid) {
|