@reconcrap/boss-recommend-mcp 2.0.20 → 2.0.21
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/package.json +1 -1
- package/src/chat-mcp.js +31 -6
- package/src/core/run/index.js +20 -1
- package/src/domains/chat/run-service.js +3 -2
- package/src/domains/recommend/run-service.js +3 -2
- package/src/domains/recruit/run-service.js +3 -2
- package/src/recommend-mcp.js +32 -7
- package/src/recruit-mcp.js +33 -7
package/package.json
CHANGED
package/src/chat-mcp.js
CHANGED
|
@@ -114,7 +114,8 @@ let chatConnectorImpl = connectChatChromeSession;
|
|
|
114
114
|
let chatJobReaderImpl = readChatJobOptionsFromSession;
|
|
115
115
|
let chatRunService = createChatRunService({
|
|
116
116
|
idPrefix: "mcp_chat",
|
|
117
|
-
workflow: (...args) => chatWorkflowImpl(...args)
|
|
117
|
+
workflow: (...args) => chatWorkflowImpl(...args),
|
|
118
|
+
onSnapshot: persistChatLifecycleSnapshot
|
|
118
119
|
});
|
|
119
120
|
const chatRunMeta = new Map();
|
|
120
121
|
|
|
@@ -387,6 +388,17 @@ function ensureChatRunArtifacts(snapshot) {
|
|
|
387
388
|
return artifacts;
|
|
388
389
|
}
|
|
389
390
|
|
|
391
|
+
function persistChatCheckpointSnapshot(normalized) {
|
|
392
|
+
const artifacts = getChatRunArtifacts(normalized?.run_id || normalized?.runId);
|
|
393
|
+
if (!artifacts) return;
|
|
394
|
+
const checkpoint = normalized?.checkpoint && typeof normalized.checkpoint === "object"
|
|
395
|
+
? normalized.checkpoint
|
|
396
|
+
: {};
|
|
397
|
+
writeJsonAtomic(artifacts.checkpoint_path, checkpoint);
|
|
398
|
+
const meta = getChatRunMeta(normalized?.run_id || normalized?.runId);
|
|
399
|
+
if (meta) meta.checkpointPath = artifacts.checkpoint_path;
|
|
400
|
+
}
|
|
401
|
+
|
|
390
402
|
function isPidAlive(pid) {
|
|
391
403
|
const numericPid = Number(pid);
|
|
392
404
|
if (!Number.isInteger(numericPid) || numericPid <= 0) return false;
|
|
@@ -565,6 +577,7 @@ function buildLegacyChatResult(snapshot) {
|
|
|
565
577
|
function normalizeRunSnapshot(snapshot) {
|
|
566
578
|
if (!snapshot) return null;
|
|
567
579
|
const meta = getChatRunMeta(snapshot.runId);
|
|
580
|
+
const artifacts = getChatRunArtifacts(snapshot.runId);
|
|
568
581
|
const summary = snapshot.summary && typeof snapshot.summary === "object" ? snapshot.summary : null;
|
|
569
582
|
const progress = normalizeLegacyProgress(snapshot.progress, summary);
|
|
570
583
|
const legacyResult = (
|
|
@@ -606,23 +619,28 @@ function normalizeRunSnapshot(snapshot) {
|
|
|
606
619
|
cancel_requested: snapshot.status === RUN_STATUS_CANCELING
|
|
607
620
|
},
|
|
608
621
|
resume: {
|
|
609
|
-
checkpoint_path: legacyResult?.checkpoint_path || null,
|
|
610
|
-
pause_control_path:
|
|
622
|
+
checkpoint_path: legacyResult?.checkpoint_path || meta.checkpointPath || artifacts?.checkpoint_path || null,
|
|
623
|
+
pause_control_path: artifacts?.run_state_path || null,
|
|
611
624
|
output_csv: legacyResult?.output_csv || null,
|
|
612
625
|
resume_count: meta.resumeCount || 0,
|
|
613
626
|
last_resumed_at: meta.lastResumedAt || null,
|
|
614
627
|
last_paused_at: snapshot.status === RUN_STATUS_PAUSED ? snapshot.updatedAt : null
|
|
615
628
|
},
|
|
616
629
|
result: legacyResult,
|
|
617
|
-
artifacts
|
|
630
|
+
artifacts
|
|
618
631
|
};
|
|
619
632
|
}
|
|
620
633
|
|
|
621
|
-
function persistChatRunSnapshot(snapshot
|
|
634
|
+
function persistChatRunSnapshot(snapshot, {
|
|
635
|
+
persistActiveCheckpoint = false
|
|
636
|
+
} = {}) {
|
|
622
637
|
const normalized = normalizeRunSnapshot(snapshot);
|
|
623
638
|
if (!normalized?.run_id) return normalized;
|
|
624
639
|
const artifacts = getChatRunArtifacts(normalized.run_id);
|
|
625
640
|
if (!artifacts) return normalized;
|
|
641
|
+
if (persistActiveCheckpoint) {
|
|
642
|
+
persistChatCheckpointSnapshot(normalized);
|
|
643
|
+
}
|
|
626
644
|
const payload = {
|
|
627
645
|
run_id: normalized.run_id,
|
|
628
646
|
mode: normalized.mode,
|
|
@@ -648,6 +666,12 @@ function persistChatRunSnapshot(snapshot) {
|
|
|
648
666
|
return normalized;
|
|
649
667
|
}
|
|
650
668
|
|
|
669
|
+
function persistChatLifecycleSnapshot(snapshot, event = {}) {
|
|
670
|
+
return persistChatRunSnapshot(snapshot, {
|
|
671
|
+
persistActiveCheckpoint: event?.type === "checkpoint"
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
651
675
|
function attachMethodEvidence(payload, runId) {
|
|
652
676
|
const meta = getChatRunMeta(runId);
|
|
653
677
|
assertNoForbiddenCdpCalls(meta.methodLog || []);
|
|
@@ -1729,7 +1753,8 @@ export function __setChatMcpWorkflowForTests(nextWorkflow) {
|
|
|
1729
1753
|
chatWorkflowImpl = typeof nextWorkflow === "function" ? nextWorkflow : runChatWorkflow;
|
|
1730
1754
|
chatRunService = createChatRunService({
|
|
1731
1755
|
idPrefix: "mcp_chat",
|
|
1732
|
-
workflow: (...args) => chatWorkflowImpl(...args)
|
|
1756
|
+
workflow: (...args) => chatWorkflowImpl(...args),
|
|
1757
|
+
onSnapshot: persistChatLifecycleSnapshot
|
|
1733
1758
|
});
|
|
1734
1759
|
}
|
|
1735
1760
|
|
package/src/core/run/index.js
CHANGED
|
@@ -65,10 +65,24 @@ function snapshotFromEntry(entry) {
|
|
|
65
65
|
|
|
66
66
|
export function createRunLifecycleManager({
|
|
67
67
|
idPrefix = "run",
|
|
68
|
-
now = nowIso
|
|
68
|
+
now = nowIso,
|
|
69
|
+
onSnapshot = null
|
|
69
70
|
} = {}) {
|
|
70
71
|
const runs = new Map();
|
|
71
72
|
|
|
73
|
+
function emitSnapshot(entry, event = {}) {
|
|
74
|
+
if (typeof onSnapshot !== "function") return;
|
|
75
|
+
try {
|
|
76
|
+
onSnapshot(snapshotFromEntry(entry), {
|
|
77
|
+
type: event.type || "update",
|
|
78
|
+
at: now(),
|
|
79
|
+
...event
|
|
80
|
+
});
|
|
81
|
+
} catch {
|
|
82
|
+
// Snapshot hooks must never interrupt an active browser run.
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
72
86
|
function getEntry(runId) {
|
|
73
87
|
const entry = runs.get(runId);
|
|
74
88
|
if (!entry) throw new Error(`Unknown runId: ${runId}`);
|
|
@@ -83,6 +97,7 @@ export function createRunLifecycleManager({
|
|
|
83
97
|
entry.run.status = status;
|
|
84
98
|
Object.assign(entry.run, patch);
|
|
85
99
|
touch(entry);
|
|
100
|
+
emitSnapshot(entry, { type: "status", status });
|
|
86
101
|
}
|
|
87
102
|
|
|
88
103
|
function createControls(entry) {
|
|
@@ -104,6 +119,7 @@ export function createRunLifecycleManager({
|
|
|
104
119
|
...progressPatch
|
|
105
120
|
};
|
|
106
121
|
touch(entry);
|
|
122
|
+
emitSnapshot(entry, { type: "progress", progressPatch });
|
|
107
123
|
return snapshotFromEntry(entry);
|
|
108
124
|
},
|
|
109
125
|
checkpoint(checkpointPatch = {}) {
|
|
@@ -113,6 +129,7 @@ export function createRunLifecycleManager({
|
|
|
113
129
|
updatedAt: now()
|
|
114
130
|
};
|
|
115
131
|
touch(entry);
|
|
132
|
+
emitSnapshot(entry, { type: "checkpoint", checkpointPatch });
|
|
116
133
|
return snapshotFromEntry(entry);
|
|
117
134
|
},
|
|
118
135
|
async waitIfPaused() {
|
|
@@ -231,6 +248,7 @@ export function createRunLifecycleManager({
|
|
|
231
248
|
entry.pauseRequested = true;
|
|
232
249
|
if (entry.run.status === RUN_STATUS_RUNNING) {
|
|
233
250
|
touch(entry);
|
|
251
|
+
emitSnapshot(entry, { type: "pause_requested" });
|
|
234
252
|
}
|
|
235
253
|
return snapshotFromEntry(entry);
|
|
236
254
|
}
|
|
@@ -246,6 +264,7 @@ export function createRunLifecycleManager({
|
|
|
246
264
|
setStatus(entry, RUN_STATUS_RUNNING);
|
|
247
265
|
} else {
|
|
248
266
|
touch(entry);
|
|
267
|
+
emitSnapshot(entry, { type: "resume_requested" });
|
|
249
268
|
}
|
|
250
269
|
return snapshotFromEntry(entry);
|
|
251
270
|
}
|
|
@@ -1564,9 +1564,10 @@ export async function runChatWorkflow({
|
|
|
1564
1564
|
export function createChatRunService({
|
|
1565
1565
|
lifecycle,
|
|
1566
1566
|
idPrefix = "chat",
|
|
1567
|
-
workflow = runChatWorkflow
|
|
1567
|
+
workflow = runChatWorkflow,
|
|
1568
|
+
onSnapshot = null
|
|
1568
1569
|
} = {}) {
|
|
1569
|
-
const manager = lifecycle || createRunLifecycleManager({ idPrefix });
|
|
1570
|
+
const manager = lifecycle || createRunLifecycleManager({ idPrefix, onSnapshot });
|
|
1570
1571
|
|
|
1571
1572
|
function startChatRun({
|
|
1572
1573
|
client,
|
|
@@ -957,9 +957,10 @@ export async function runRecommendWorkflow({
|
|
|
957
957
|
export function createRecommendRunService({
|
|
958
958
|
lifecycle,
|
|
959
959
|
idPrefix = "recommend",
|
|
960
|
-
workflow = runRecommendWorkflow
|
|
960
|
+
workflow = runRecommendWorkflow,
|
|
961
|
+
onSnapshot = null
|
|
961
962
|
} = {}) {
|
|
962
|
-
const manager = lifecycle || createRunLifecycleManager({ idPrefix });
|
|
963
|
+
const manager = lifecycle || createRunLifecycleManager({ idPrefix, onSnapshot });
|
|
963
964
|
|
|
964
965
|
function startRecommendRun({
|
|
965
966
|
client,
|
|
@@ -619,9 +619,10 @@ export async function runRecruitWorkflow({
|
|
|
619
619
|
export function createRecruitRunService({
|
|
620
620
|
lifecycle,
|
|
621
621
|
idPrefix = "recruit",
|
|
622
|
-
workflow = runRecruitWorkflow
|
|
622
|
+
workflow = runRecruitWorkflow,
|
|
623
|
+
onSnapshot = null
|
|
623
624
|
} = {}) {
|
|
624
|
-
const manager = lifecycle || createRunLifecycleManager({ idPrefix });
|
|
625
|
+
const manager = lifecycle || createRunLifecycleManager({ idPrefix, onSnapshot });
|
|
625
626
|
|
|
626
627
|
function startRecruitRun({
|
|
627
628
|
client,
|
package/src/recommend-mcp.js
CHANGED
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
const DEFAULT_RECOMMEND_HOST = "127.0.0.1";
|
|
53
53
|
const DEFAULT_RECOMMEND_PORT = 9222;
|
|
54
54
|
const DEFAULT_RECOMMEND_POLL_AFTER_SEC = 10;
|
|
55
|
-
const TARGET_COUNT_SEMANTICS = "target_count means
|
|
55
|
+
const TARGET_COUNT_SEMANTICS = "target_count means candidates that pass screening; scan continues until that many candidates pass or the list ends";
|
|
56
56
|
const RUN_MODE_ASYNC = "async";
|
|
57
57
|
|
|
58
58
|
const TERMINAL_STATUSES = new Set([
|
|
@@ -66,7 +66,8 @@ let recommendConnectorImpl = connectRecommendChromeSession;
|
|
|
66
66
|
let recommendJobReaderImpl = readRecommendJobOptionsFromSession;
|
|
67
67
|
let recommendRunService = createRecommendRunService({
|
|
68
68
|
idPrefix: "mcp_recommend",
|
|
69
|
-
workflow: (...args) => recommendWorkflowImpl(...args)
|
|
69
|
+
workflow: (...args) => recommendWorkflowImpl(...args),
|
|
70
|
+
onSnapshot: persistRecommendLifecycleSnapshot
|
|
70
71
|
});
|
|
71
72
|
const recommendRunMeta = new Map();
|
|
72
73
|
|
|
@@ -328,6 +329,17 @@ function ensureRecommendRunArtifacts(snapshot) {
|
|
|
328
329
|
return artifacts;
|
|
329
330
|
}
|
|
330
331
|
|
|
332
|
+
function persistRecommendCheckpointSnapshot(normalized) {
|
|
333
|
+
const artifacts = getRecommendRunArtifacts(normalized?.run_id || normalized?.runId);
|
|
334
|
+
if (!artifacts) return;
|
|
335
|
+
const checkpoint = normalized?.checkpoint && typeof normalized.checkpoint === "object"
|
|
336
|
+
? normalized.checkpoint
|
|
337
|
+
: {};
|
|
338
|
+
writeJsonAtomic(artifacts.checkpoint_path, checkpoint);
|
|
339
|
+
const meta = getRecommendRunMeta(normalized?.run_id || normalized?.runId);
|
|
340
|
+
if (meta) meta.checkpointPath = artifacts.checkpoint_path;
|
|
341
|
+
}
|
|
342
|
+
|
|
331
343
|
function buildLegacyRecommendResult(snapshot) {
|
|
332
344
|
if (!snapshot) return null;
|
|
333
345
|
const artifacts = ensureRecommendRunArtifacts(snapshot);
|
|
@@ -390,6 +402,7 @@ function buildLegacyRecommendResult(snapshot) {
|
|
|
390
402
|
function normalizeRunSnapshot(snapshot) {
|
|
391
403
|
if (!snapshot) return null;
|
|
392
404
|
const meta = getRecommendRunMeta(snapshot.runId);
|
|
405
|
+
const artifacts = getRecommendRunArtifacts(snapshot.runId);
|
|
393
406
|
const summary = snapshot.summary && typeof snapshot.summary === "object" ? snapshot.summary : null;
|
|
394
407
|
const progress = normalizeLegacyProgress(snapshot.progress, summary);
|
|
395
408
|
const legacyResult = (
|
|
@@ -429,23 +442,28 @@ function normalizeRunSnapshot(snapshot) {
|
|
|
429
442
|
cancel_requested: snapshot.status === RUN_STATUS_CANCELING
|
|
430
443
|
},
|
|
431
444
|
resume: {
|
|
432
|
-
checkpoint_path: legacyResult?.checkpoint_path || null,
|
|
433
|
-
pause_control_path:
|
|
445
|
+
checkpoint_path: legacyResult?.checkpoint_path || meta.checkpointPath || artifacts?.checkpoint_path || null,
|
|
446
|
+
pause_control_path: artifacts?.run_state_path || null,
|
|
434
447
|
output_csv: legacyResult?.output_csv || null,
|
|
435
448
|
resume_count: meta.resumeCount || 0,
|
|
436
449
|
last_resumed_at: meta.lastResumedAt || null,
|
|
437
450
|
last_paused_at: snapshot.status === RUN_STATUS_PAUSED ? snapshot.updatedAt : null
|
|
438
451
|
},
|
|
439
452
|
result: legacyResult,
|
|
440
|
-
artifacts
|
|
453
|
+
artifacts
|
|
441
454
|
};
|
|
442
455
|
}
|
|
443
456
|
|
|
444
|
-
function persistRecommendRunSnapshot(snapshot
|
|
457
|
+
function persistRecommendRunSnapshot(snapshot, {
|
|
458
|
+
persistActiveCheckpoint = false
|
|
459
|
+
} = {}) {
|
|
445
460
|
const normalized = normalizeRunSnapshot(snapshot);
|
|
446
461
|
if (!normalized?.run_id) return normalized;
|
|
447
462
|
const artifacts = getRecommendRunArtifacts(normalized.run_id);
|
|
448
463
|
if (!artifacts) return normalized;
|
|
464
|
+
if (persistActiveCheckpoint) {
|
|
465
|
+
persistRecommendCheckpointSnapshot(normalized);
|
|
466
|
+
}
|
|
449
467
|
const payload = {
|
|
450
468
|
run_id: normalized.run_id,
|
|
451
469
|
mode: normalized.mode,
|
|
@@ -471,6 +489,12 @@ function persistRecommendRunSnapshot(snapshot) {
|
|
|
471
489
|
return normalized;
|
|
472
490
|
}
|
|
473
491
|
|
|
492
|
+
function persistRecommendLifecycleSnapshot(snapshot, event = {}) {
|
|
493
|
+
return persistRecommendRunSnapshot(snapshot, {
|
|
494
|
+
persistActiveCheckpoint: event?.type === "checkpoint"
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
474
498
|
function attachMethodEvidence(payload, runId) {
|
|
475
499
|
const meta = getRecommendRunMeta(runId);
|
|
476
500
|
assertNoForbiddenCdpCalls(meta.methodLog || []);
|
|
@@ -1440,7 +1464,8 @@ export function __setRecommendMcpWorkflowForTests(nextWorkflow) {
|
|
|
1440
1464
|
recommendWorkflowImpl = typeof nextWorkflow === "function" ? nextWorkflow : runRecommendWorkflow;
|
|
1441
1465
|
recommendRunService = createRecommendRunService({
|
|
1442
1466
|
idPrefix: "mcp_recommend",
|
|
1443
|
-
workflow: (...args) => recommendWorkflowImpl(...args)
|
|
1467
|
+
workflow: (...args) => recommendWorkflowImpl(...args),
|
|
1468
|
+
onSnapshot: persistRecommendLifecycleSnapshot
|
|
1444
1469
|
});
|
|
1445
1470
|
}
|
|
1446
1471
|
|
package/src/recruit-mcp.js
CHANGED
|
@@ -42,7 +42,7 @@ const RUN_MODE_SYNC = "sync";
|
|
|
42
42
|
const DEFAULT_RECRUIT_POLL_AFTER_SEC = 10;
|
|
43
43
|
const DEFAULT_RECRUIT_HOST = "127.0.0.1";
|
|
44
44
|
const DEFAULT_RECRUIT_PORT = 9222;
|
|
45
|
-
const TARGET_COUNT_SEMANTICS = "target_count means
|
|
45
|
+
const TARGET_COUNT_SEMANTICS = "target_count means candidates that pass screening; scan continues until that many candidates pass or the list ends";
|
|
46
46
|
const DEFAULT_RECRUIT_HOME_DIR = ".boss-recruit-mcp";
|
|
47
47
|
|
|
48
48
|
const TERMINAL_STATUSES = new Set([
|
|
@@ -55,7 +55,8 @@ let recruitWorkflowImpl = runRecruitWorkflow;
|
|
|
55
55
|
let recruitConnectorImpl = connectRecruitChromeSession;
|
|
56
56
|
let recruitRunService = createRecruitRunService({
|
|
57
57
|
idPrefix: "mcp_recruit",
|
|
58
|
-
workflow: (...args) => recruitWorkflowImpl(...args)
|
|
58
|
+
workflow: (...args) => recruitWorkflowImpl(...args),
|
|
59
|
+
onSnapshot: persistRecruitLifecycleSnapshot
|
|
59
60
|
});
|
|
60
61
|
const recruitRunMeta = new Map();
|
|
61
62
|
|
|
@@ -253,6 +254,17 @@ function ensureRecruitRunArtifacts(snapshot) {
|
|
|
253
254
|
return artifacts;
|
|
254
255
|
}
|
|
255
256
|
|
|
257
|
+
function persistRecruitCheckpointSnapshot(normalized) {
|
|
258
|
+
const artifacts = getRecruitRunArtifacts(normalized?.run_id || normalized?.runId);
|
|
259
|
+
if (!artifacts) return;
|
|
260
|
+
const checkpoint = normalized?.checkpoint && typeof normalized.checkpoint === "object"
|
|
261
|
+
? normalized.checkpoint
|
|
262
|
+
: {};
|
|
263
|
+
writeJsonAtomic(artifacts.checkpoint_path, checkpoint);
|
|
264
|
+
const meta = getRecruitRunMeta(normalized?.run_id || normalized?.runId);
|
|
265
|
+
if (meta) meta.checkpointPath = artifacts.checkpoint_path;
|
|
266
|
+
}
|
|
267
|
+
|
|
256
268
|
function toIsoOrNull(value) {
|
|
257
269
|
const normalized = normalizeText(value);
|
|
258
270
|
return normalized || null;
|
|
@@ -565,6 +577,7 @@ function evaluateRecruitPipelineGate(parsed) {
|
|
|
565
577
|
function normalizeRunSnapshot(snapshot) {
|
|
566
578
|
if (!snapshot) return null;
|
|
567
579
|
const meta = getRecruitRunMeta(snapshot.runId);
|
|
580
|
+
const artifacts = getRecruitRunArtifacts(snapshot.runId);
|
|
568
581
|
const summary = snapshot.summary && typeof snapshot.summary === "object" ? snapshot.summary : null;
|
|
569
582
|
const progress = normalizeLegacyProgress(snapshot.progress, summary);
|
|
570
583
|
const legacyResult = (
|
|
@@ -603,27 +616,33 @@ function normalizeRunSnapshot(snapshot) {
|
|
|
603
616
|
cancel_requested: snapshot.status === RUN_STATUS_CANCELING
|
|
604
617
|
},
|
|
605
618
|
resume: {
|
|
606
|
-
checkpoint_path: legacyResult?.checkpoint_path || null,
|
|
607
|
-
pause_control_path:
|
|
619
|
+
checkpoint_path: legacyResult?.checkpoint_path || meta.checkpointPath || artifacts?.checkpoint_path || null,
|
|
620
|
+
pause_control_path: artifacts?.run_state_path || null,
|
|
608
621
|
output_csv: legacyResult?.output_csv || null,
|
|
609
622
|
resume_count: meta.resumeCount || 0,
|
|
610
623
|
last_resumed_at: meta.lastResumedAt || null,
|
|
611
624
|
last_paused_at: snapshot.status === RUN_STATUS_PAUSED ? snapshot.updatedAt : null
|
|
612
625
|
},
|
|
613
626
|
result: legacyResult,
|
|
614
|
-
artifacts
|
|
627
|
+
artifacts
|
|
615
628
|
};
|
|
616
629
|
}
|
|
617
630
|
|
|
618
|
-
function persistRecruitRunSnapshot(snapshot
|
|
631
|
+
function persistRecruitRunSnapshot(snapshot, {
|
|
632
|
+
persistActiveCheckpoint = false
|
|
633
|
+
} = {}) {
|
|
619
634
|
const normalized = normalizeRunSnapshot(snapshot);
|
|
620
635
|
if (!normalized?.run_id) return normalized;
|
|
621
636
|
const artifacts = getRecruitRunArtifacts(normalized.run_id);
|
|
622
637
|
if (!artifacts) return normalized;
|
|
638
|
+
if (persistActiveCheckpoint) {
|
|
639
|
+
persistRecruitCheckpointSnapshot(normalized);
|
|
640
|
+
}
|
|
623
641
|
const payload = {
|
|
624
642
|
run_id: normalized.run_id,
|
|
625
643
|
mode: normalized.mode,
|
|
626
644
|
state: normalized.state,
|
|
645
|
+
status: normalized.status,
|
|
627
646
|
stage: normalized.stage,
|
|
628
647
|
started_at: normalized.started_at,
|
|
629
648
|
updated_at: normalized.updated_at,
|
|
@@ -644,6 +663,12 @@ function persistRecruitRunSnapshot(snapshot) {
|
|
|
644
663
|
return normalized;
|
|
645
664
|
}
|
|
646
665
|
|
|
666
|
+
function persistRecruitLifecycleSnapshot(snapshot, event = {}) {
|
|
667
|
+
return persistRecruitRunSnapshot(snapshot, {
|
|
668
|
+
persistActiveCheckpoint: event?.type === "checkpoint"
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
|
|
647
672
|
function getRecruitRunMeta(runId) {
|
|
648
673
|
return recruitRunMeta.get(runId) || {};
|
|
649
674
|
}
|
|
@@ -1248,7 +1273,8 @@ export function __setRecruitMcpWorkflowForTests(nextWorkflow) {
|
|
|
1248
1273
|
recruitWorkflowImpl = typeof nextWorkflow === "function" ? nextWorkflow : runRecruitWorkflow;
|
|
1249
1274
|
recruitRunService = createRecruitRunService({
|
|
1250
1275
|
idPrefix: "mcp_recruit",
|
|
1251
|
-
workflow: (...args) => recruitWorkflowImpl(...args)
|
|
1276
|
+
workflow: (...args) => recruitWorkflowImpl(...args),
|
|
1277
|
+
onSnapshot: persistRecruitLifecycleSnapshot
|
|
1252
1278
|
});
|
|
1253
1279
|
}
|
|
1254
1280
|
|