@lcv-ideas-software/cross-review 4.0.8 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +204 -0
- package/README.md +74 -72
- package/dist/scripts/runtime-smoke.js +10 -3
- package/dist/scripts/runtime-smoke.js.map +1 -1
- package/dist/scripts/smoke.js +200 -79
- package/dist/scripts/smoke.js.map +1 -1
- package/dist/src/core/cache-manifest.d.ts +2 -2
- package/dist/src/core/cache-manifest.js +15 -9
- package/dist/src/core/cache-manifest.js.map +1 -1
- package/dist/src/core/config.d.ts +2 -2
- package/dist/src/core/config.js +2 -2
- package/dist/src/core/orchestrator.js +63 -63
- package/dist/src/core/orchestrator.js.map +1 -1
- package/dist/src/core/session-store.d.ts +35 -34
- package/dist/src/core/session-store.js +268 -157
- package/dist/src/core/session-store.js.map +1 -1
- package/dist/src/dashboard/server.js +5 -1
- package/dist/src/dashboard/server.js.map +1 -1
- package/dist/src/mcp/server.js +41 -33
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/security/redact.js +13 -2
- package/dist/src/security/redact.js.map +1 -1
- package/package.json +3 -1
|
@@ -11,7 +11,7 @@ export declare function readCacheManifest(dataDir: string, sessionId: string): C
|
|
|
11
11
|
* by tests; production callers should append entries via
|
|
12
12
|
* appendCacheManifestEntry.
|
|
13
13
|
*/
|
|
14
|
-
export declare function writeCacheManifest(dataDir: string, sessionId: string, manifest: CacheManifest): void
|
|
14
|
+
export declare function writeCacheManifest(dataDir: string, sessionId: string, manifest: CacheManifest): Promise<void>;
|
|
15
15
|
/**
|
|
16
16
|
* Append a single entry to the session manifest. Lazily creates the
|
|
17
17
|
* manifest if it does not exist. Each call performs (a) read-current,
|
|
@@ -19,4 +19,4 @@ export declare function writeCacheManifest(dataDir: string, sessionId: string, m
|
|
|
19
19
|
* process; concurrent calls in the same process must be awaited in
|
|
20
20
|
* order by the caller.
|
|
21
21
|
*/
|
|
22
|
-
export declare function appendCacheManifestEntry(dataDir: string, sessionId: string, entry: CacheManifestEntry, cacheSchemaVersion?: string): void
|
|
22
|
+
export declare function appendCacheManifestEntry(dataDir: string, sessionId: string, entry: CacheManifestEntry, cacheSchemaVersion?: string): Promise<void>;
|
|
@@ -23,7 +23,7 @@ const TMP_NONCE_BYTES = 2;
|
|
|
23
23
|
function manifestPath(dataDir, sessionId) {
|
|
24
24
|
return path.resolve(dataDir, "sessions", sessionId, MANIFEST_FILENAME);
|
|
25
25
|
}
|
|
26
|
-
function writeJsonAtomic(file, data) {
|
|
26
|
+
async function writeJsonAtomic(file, data) {
|
|
27
27
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
28
28
|
const nonce = crypto.randomBytes(TMP_NONCE_BYTES).toString("hex");
|
|
29
29
|
const tmp = `${file}.${process.pid}.${Date.now()}.${nonce}.tmp`;
|
|
@@ -39,11 +39,17 @@ function writeJsonAtomic(file, data) {
|
|
|
39
39
|
const code = err.code;
|
|
40
40
|
if (!code || !ATOMIC_WRITE_RETRY_CODES.has(code))
|
|
41
41
|
break;
|
|
42
|
+
// v4.1.0 hardening (Codex R1 catch on the broader F3 grep): this
|
|
43
|
+
// second atomic-write helper had the SAME CPU-burning busy-wait
|
|
44
|
+
// as session-store.ts writeJson (now fixed). The single Node
|
|
45
|
+
// event loop was being blocked for up to 310 ms (10+20+40+80+160)
|
|
46
|
+
// per cache_manifest append under Windows-AV-induced
|
|
47
|
+
// EPERM/EBUSY contention. Promise + setTimeout: event loop
|
|
48
|
+
// remains fully responsive.
|
|
42
49
|
const wait = 10 * 2 ** attempt;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
50
|
+
await new Promise((resolve) => {
|
|
51
|
+
setTimeout(resolve, wait);
|
|
52
|
+
});
|
|
47
53
|
}
|
|
48
54
|
}
|
|
49
55
|
try {
|
|
@@ -77,8 +83,8 @@ export function readCacheManifest(dataDir, sessionId) {
|
|
|
77
83
|
* by tests; production callers should append entries via
|
|
78
84
|
* appendCacheManifestEntry.
|
|
79
85
|
*/
|
|
80
|
-
export function writeCacheManifest(dataDir, sessionId, manifest) {
|
|
81
|
-
writeJsonAtomic(manifestPath(dataDir, sessionId), manifest);
|
|
86
|
+
export async function writeCacheManifest(dataDir, sessionId, manifest) {
|
|
87
|
+
await writeJsonAtomic(manifestPath(dataDir, sessionId), manifest);
|
|
82
88
|
}
|
|
83
89
|
/**
|
|
84
90
|
* Append a single entry to the session manifest. Lazily creates the
|
|
@@ -87,7 +93,7 @@ export function writeCacheManifest(dataDir, sessionId, manifest) {
|
|
|
87
93
|
* process; concurrent calls in the same process must be awaited in
|
|
88
94
|
* order by the caller.
|
|
89
95
|
*/
|
|
90
|
-
export function appendCacheManifestEntry(dataDir, sessionId, entry, cacheSchemaVersion = CACHE_SCHEMA_VERSION_DEFAULT) {
|
|
96
|
+
export async function appendCacheManifestEntry(dataDir, sessionId, entry, cacheSchemaVersion = CACHE_SCHEMA_VERSION_DEFAULT) {
|
|
91
97
|
const file = manifestPath(dataDir, sessionId);
|
|
92
98
|
const nowIso = new Date().toISOString();
|
|
93
99
|
let current;
|
|
@@ -128,6 +134,6 @@ export function appendCacheManifestEntry(dataDir, sessionId, entry, cacheSchemaV
|
|
|
128
134
|
}
|
|
129
135
|
current.entries.push(entry);
|
|
130
136
|
current.updated_at = nowIso;
|
|
131
|
-
writeJsonAtomic(file, current);
|
|
137
|
+
await writeJsonAtomic(file, current);
|
|
132
138
|
}
|
|
133
139
|
//# sourceMappingURL=cache-manifest.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-manifest.js","sourceRoot":"","sources":["../../../src/core/cache-manifest.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,+DAA+D;AAC/D,EAAE;AACF,wEAAwE;AACxE,mEAAmE;AACnE,wEAAwE;AACxE,mEAAmE;AACnE,sCAAsC;AACtC,EAAE;AACF,oEAAoE;AACpE,iEAAiE;AACjE,oEAAoE;AACpE,sDAAsD;AAEtD,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AACjD,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAChD,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AACjF,MAAM,yBAAyB,GAAG,CAAC,CAAC;AACpC,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,SAAS,YAAY,CAAC,OAAe,EAAE,SAAiB;IACtD,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC;AACzE,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"cache-manifest.js","sourceRoot":"","sources":["../../../src/core/cache-manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AppConfig, PeerId } from "./types.js";
|
|
2
|
-
export declare const VERSION = "4.
|
|
3
|
-
export declare const RELEASE_DATE = "2026-05-
|
|
2
|
+
export declare const VERSION = "4.1.1";
|
|
3
|
+
export declare const RELEASE_DATE = "2026-05-17";
|
|
4
4
|
export declare const DEFAULT_MAX_OUTPUT_TOKENS = 20000;
|
|
5
5
|
export declare function getLastFileConfigResult(): import("./file-config.js").ApplyFileConfigResult | undefined;
|
|
6
6
|
export declare function loadConfig(): AppConfig;
|
package/dist/src/core/config.js
CHANGED
|
@@ -17,8 +17,8 @@ function expandHome(rawPath) {
|
|
|
17
17
|
}
|
|
18
18
|
return rawPath;
|
|
19
19
|
}
|
|
20
|
-
export const VERSION = "4.
|
|
21
|
-
export const RELEASE_DATE = "2026-05-
|
|
20
|
+
export const VERSION = "4.1.1";
|
|
21
|
+
export const RELEASE_DATE = "2026-05-17";
|
|
22
22
|
export const DEFAULT_MAX_OUTPUT_TOKENS = 20_000;
|
|
23
23
|
const COST_RATE_ENV_PREFIX = {
|
|
24
24
|
codex: "CROSS_REVIEW_OPENAI",
|
|
@@ -1005,7 +1005,7 @@ export class CrossReviewOrchestrator {
|
|
|
1005
1005
|
per_peer_verdict: perPeerVerdict,
|
|
1006
1006
|
});
|
|
1007
1007
|
if (unanimousVerifiedSatisfied && mode === "active") {
|
|
1008
|
-
const result = this.store.markEvidenceItemAddressedByJudge(params.session_id, item.id, {
|
|
1008
|
+
const result = await this.store.markEvidenceItemAddressedByJudge(params.session_id, item.id, {
|
|
1009
1009
|
round: judgmentRound,
|
|
1010
1010
|
rationale: Object.values(rationales).join(" || "),
|
|
1011
1011
|
judge_peer: params.judge_peers[0],
|
|
@@ -1231,7 +1231,7 @@ export class CrossReviewOrchestrator {
|
|
|
1231
1231
|
});
|
|
1232
1232
|
}
|
|
1233
1233
|
else {
|
|
1234
|
-
const result = this.store.markEvidenceItemAddressedByJudge(params.session_id, item.id, {
|
|
1234
|
+
const result = await this.store.markEvidenceItemAddressedByJudge(params.session_id, item.id, {
|
|
1235
1235
|
round: judgmentRound,
|
|
1236
1236
|
rationale: judgment.rationale,
|
|
1237
1237
|
judge_peer: params.judge_peer,
|
|
@@ -1375,7 +1375,7 @@ export class CrossReviewOrchestrator {
|
|
|
1375
1375
|
async initSession(task, caller = "operator", reviewFocus) {
|
|
1376
1376
|
const snapshot = await this.probeAll();
|
|
1377
1377
|
const normalizedReviewFocus = normalizeReviewFocus(reviewFocus, this.config);
|
|
1378
|
-
const meta = this.store.init(task, caller, snapshot, normalizedReviewFocus);
|
|
1378
|
+
const meta = await this.store.init(task, caller, snapshot, normalizedReviewFocus);
|
|
1379
1379
|
this.emit({
|
|
1380
1380
|
type: "session.created",
|
|
1381
1381
|
session_id: meta.session_id,
|
|
@@ -1393,7 +1393,7 @@ export class CrossReviewOrchestrator {
|
|
|
1393
1393
|
.filter((model) => model && model !== adapter.model)
|
|
1394
1394
|
.map((model) => createAdapters(this.config, { [adapter.id]: model })[adapter.id]);
|
|
1395
1395
|
}
|
|
1396
|
-
recordFallback(sessionId, adapter, fallback, reason) {
|
|
1396
|
+
async recordFallback(sessionId, adapter, fallback, reason) {
|
|
1397
1397
|
const event = {
|
|
1398
1398
|
peer: adapter.id,
|
|
1399
1399
|
provider: adapter.provider,
|
|
@@ -1402,7 +1402,7 @@ export class CrossReviewOrchestrator {
|
|
|
1402
1402
|
reason,
|
|
1403
1403
|
ts: now(),
|
|
1404
1404
|
};
|
|
1405
|
-
this.store.appendFallbackEvent(sessionId, event);
|
|
1405
|
+
await this.store.appendFallbackEvent(sessionId, event);
|
|
1406
1406
|
this.emit({
|
|
1407
1407
|
type: "peer.fallback.started",
|
|
1408
1408
|
session_id: sessionId,
|
|
@@ -1416,7 +1416,7 @@ export class CrossReviewOrchestrator {
|
|
|
1416
1416
|
// peer call surfaced cache telemetry, and append a row to the
|
|
1417
1417
|
// session cache manifest. Best-effort; never throws — manifest
|
|
1418
1418
|
// failures should not break the review loop.
|
|
1419
|
-
recordCacheTelemetry(sessionId, round, peerResult) {
|
|
1419
|
+
async recordCacheTelemetry(sessionId, round, peerResult) {
|
|
1420
1420
|
try {
|
|
1421
1421
|
if (!this.config.cache.enabled)
|
|
1422
1422
|
return;
|
|
@@ -1449,7 +1449,7 @@ export class CrossReviewOrchestrator {
|
|
|
1449
1449
|
savings_unknown: savings.unknown,
|
|
1450
1450
|
},
|
|
1451
1451
|
});
|
|
1452
|
-
appendCacheManifestEntry(this.config.data_dir, sessionId, {
|
|
1452
|
+
await appendCacheManifestEntry(this.config.data_dir, sessionId, {
|
|
1453
1453
|
ts: new Date().toISOString(),
|
|
1454
1454
|
round,
|
|
1455
1455
|
peer: peerResult.peer,
|
|
@@ -1478,7 +1478,7 @@ export class CrossReviewOrchestrator {
|
|
|
1478
1478
|
// session has no ceiling, when cumulative cost is below threshold, or
|
|
1479
1479
|
// when the warning has already fired. Best-effort writeback — manifest
|
|
1480
1480
|
// failures should not break the review loop.
|
|
1481
|
-
checkBudgetWarning(sessionId, round) {
|
|
1481
|
+
async checkBudgetWarning(sessionId, round) {
|
|
1482
1482
|
try {
|
|
1483
1483
|
const meta = this.store.read(sessionId);
|
|
1484
1484
|
const ceiling = meta.cost_ceiling_usd;
|
|
@@ -1493,7 +1493,7 @@ export class CrossReviewOrchestrator {
|
|
|
1493
1493
|
// Persist the one-shot guard FIRST so an emit-throw cannot cause
|
|
1494
1494
|
// re-emission on a retry; we accept "warning persisted but emit
|
|
1495
1495
|
// observably failed" as the safer drift mode.
|
|
1496
|
-
this.store.markBudgetWarningEmitted(sessionId);
|
|
1496
|
+
await this.store.markBudgetWarningEmitted(sessionId);
|
|
1497
1497
|
this.emit({
|
|
1498
1498
|
type: "session.budget_warning",
|
|
1499
1499
|
session_id: sessionId,
|
|
@@ -1529,7 +1529,7 @@ export class CrossReviewOrchestrator {
|
|
|
1529
1529
|
let lastFallbackFailure;
|
|
1530
1530
|
for (const fallback of this.fallbackAdapters(adapter)) {
|
|
1531
1531
|
fallbackWasTried = true;
|
|
1532
|
-
const fallbackEvent = this.recordFallback(context.session_id, adapter, fallback, failure.failure_class);
|
|
1532
|
+
const fallbackEvent = await this.recordFallback(context.session_id, adapter, fallback, failure.failure_class);
|
|
1533
1533
|
// v2.5.0 fix (Codex audit P3, 2026-05-03): every paid retry path
|
|
1534
1534
|
// must emit a cost_alert so FinOps consumers can preregister
|
|
1535
1535
|
// unexpected spend. Pre-v2.5.0 only `peer.format_recovery`
|
|
@@ -1802,7 +1802,7 @@ export class CrossReviewOrchestrator {
|
|
|
1802
1802
|
const session = existingSession
|
|
1803
1803
|
? existingSession
|
|
1804
1804
|
: missingFinancialVars.length
|
|
1805
|
-
? this.store.init(input.task, effectivePetitioner, [], normalizeReviewFocus(input.review_focus, this.config))
|
|
1805
|
+
? await this.store.init(input.task, effectivePetitioner, [], normalizeReviewFocus(input.review_focus, this.config))
|
|
1806
1806
|
: await this.initSession(input.task, effectivePetitioner, input.review_focus);
|
|
1807
1807
|
const petitioner = effectivePetitioner;
|
|
1808
1808
|
const roundNumber = session.rounds.length + 1;
|
|
@@ -1845,7 +1845,7 @@ export class CrossReviewOrchestrator {
|
|
|
1845
1845
|
const prompt = buildReviewPrompt(session, input.draft, this.config, input.review_focus, attachments);
|
|
1846
1846
|
const moderationSafePrompt = buildModerationSafeReviewPrompt(session, input.draft, this.config, input.review_focus);
|
|
1847
1847
|
const promptFile = this.store.savePrompt(session.session_id, roundNumber, prompt);
|
|
1848
|
-
this.store.markInFlight(session.session_id, {
|
|
1848
|
+
await this.store.markInFlight(session.session_id, {
|
|
1849
1849
|
round: roundNumber,
|
|
1850
1850
|
peers: selectedPeers,
|
|
1851
1851
|
started_at: startedAt,
|
|
@@ -1862,10 +1862,10 @@ export class CrossReviewOrchestrator {
|
|
|
1862
1862
|
const message = financialControlsMissingMessage(missingFinancialVars);
|
|
1863
1863
|
const rejected = selectAdapters(adapters, selectedPeers).map((adapter) => budgetPreflightFailure(adapter.id, adapter.provider, adapter.model, message));
|
|
1864
1864
|
for (const failure of rejected) {
|
|
1865
|
-
this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
1865
|
+
await this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
1866
1866
|
}
|
|
1867
1867
|
const convergence = checkConvergence(selectedPeers, callerStatus, [], rejected);
|
|
1868
|
-
const round = this.store.appendRound(session.session_id, {
|
|
1868
|
+
const round = await this.store.appendRound(session.session_id, {
|
|
1869
1869
|
caller_status: callerStatus,
|
|
1870
1870
|
draft_file: draftFile,
|
|
1871
1871
|
prompt_file: promptFile,
|
|
@@ -1875,7 +1875,7 @@ export class CrossReviewOrchestrator {
|
|
|
1875
1875
|
convergence_scope: convergenceScope,
|
|
1876
1876
|
started_at: startedAt,
|
|
1877
1877
|
});
|
|
1878
|
-
const updated = this.store.finalize(session.session_id, "max-rounds", "financial_controls_missing");
|
|
1878
|
+
const updated = await this.store.finalize(session.session_id, "max-rounds", "financial_controls_missing");
|
|
1879
1879
|
this.emit({
|
|
1880
1880
|
type: "round.blocked.financial_controls_missing",
|
|
1881
1881
|
session_id: session.session_id,
|
|
@@ -1904,10 +1904,10 @@ export class CrossReviewOrchestrator {
|
|
|
1904
1904
|
if (message) {
|
|
1905
1905
|
const rejected = selectAdapters(adapters, selectedPeers).map((adapter) => budgetPreflightFailure(adapter.id, adapter.provider, adapter.model, message));
|
|
1906
1906
|
for (const failure of rejected) {
|
|
1907
|
-
this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
1907
|
+
await this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
1908
1908
|
}
|
|
1909
1909
|
const convergence = checkConvergence(selectedPeers, callerStatus, [], rejected);
|
|
1910
|
-
const round = this.store.appendRound(session.session_id, {
|
|
1910
|
+
const round = await this.store.appendRound(session.session_id, {
|
|
1911
1911
|
caller_status: callerStatus,
|
|
1912
1912
|
draft_file: draftFile,
|
|
1913
1913
|
prompt_file: promptFile,
|
|
@@ -1917,7 +1917,7 @@ export class CrossReviewOrchestrator {
|
|
|
1917
1917
|
convergence_scope: convergenceScope,
|
|
1918
1918
|
started_at: startedAt,
|
|
1919
1919
|
});
|
|
1920
|
-
const updated = this.store.finalize(session.session_id, "max-rounds", "budget_preflight");
|
|
1920
|
+
const updated = await this.store.finalize(session.session_id, "max-rounds", "budget_preflight");
|
|
1921
1921
|
this.emit({
|
|
1922
1922
|
type: "round.blocked.budget_preflight",
|
|
1923
1923
|
session_id: session.session_id,
|
|
@@ -1935,7 +1935,7 @@ export class CrossReviewOrchestrator {
|
|
|
1935
1935
|
}
|
|
1936
1936
|
if (this.isCancelled(session.session_id, input.signal)) {
|
|
1937
1937
|
const rejected = selectAdapters(adapters, selectedPeers).map((adapter) => cancellationFailure(adapter.id, adapter.provider, adapter.model, "Session cancellation was requested before this round started."));
|
|
1938
|
-
const round = this.store.appendRound(session.session_id, {
|
|
1938
|
+
const round = await this.store.appendRound(session.session_id, {
|
|
1939
1939
|
caller_status: callerStatus,
|
|
1940
1940
|
draft_file: draftFile,
|
|
1941
1941
|
prompt_file: promptFile,
|
|
@@ -1945,7 +1945,7 @@ export class CrossReviewOrchestrator {
|
|
|
1945
1945
|
convergence_scope: convergenceScope,
|
|
1946
1946
|
started_at: startedAt,
|
|
1947
1947
|
});
|
|
1948
|
-
const updated = this.store.markCancelled(session.session_id, "session_cancelled");
|
|
1948
|
+
const updated = await this.store.markCancelled(session.session_id, "session_cancelled");
|
|
1949
1949
|
return { session: updated, round, converged: false };
|
|
1950
1950
|
}
|
|
1951
1951
|
const settled = await Promise.all(selectAdapters(adapters, selectedPeers).map((adapter) => this.callPeerForReview(adapter, prompt, moderationSafePrompt, {
|
|
@@ -2027,14 +2027,14 @@ export class CrossReviewOrchestrator {
|
|
|
2027
2027
|
latency_ms: peerResult.latency_ms,
|
|
2028
2028
|
};
|
|
2029
2029
|
rejected.push(failure);
|
|
2030
|
-
this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2030
|
+
await this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2031
2031
|
peers.push(peerResult);
|
|
2032
|
-
this.store.savePeerResult(session.session_id, roundNumber, peerResult);
|
|
2032
|
+
await this.store.savePeerResult(session.session_id, roundNumber, peerResult);
|
|
2033
2033
|
continue;
|
|
2034
2034
|
}
|
|
2035
2035
|
recoveriesUsedThisCall += 1;
|
|
2036
2036
|
const decisionRetry = !containsReviewDecisionLexeme(peerResult.text);
|
|
2037
|
-
this.store.savePeerResult(session.session_id, roundNumber, peerResult, "unparsed-response");
|
|
2037
|
+
await this.store.savePeerResult(session.session_id, roundNumber, peerResult, "unparsed-response");
|
|
2038
2038
|
this.emit({
|
|
2039
2039
|
type: "peer.format_recovery.started",
|
|
2040
2040
|
session_id: session.session_id,
|
|
@@ -2093,7 +2093,7 @@ export class CrossReviewOrchestrator {
|
|
|
2093
2093
|
latency_ms: peerResult.latency_ms,
|
|
2094
2094
|
};
|
|
2095
2095
|
rejected.push(failure);
|
|
2096
|
-
this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2096
|
+
await this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2097
2097
|
this.emit({
|
|
2098
2098
|
type: "peer.format_recovery.budget_blocked",
|
|
2099
2099
|
session_id: session.session_id,
|
|
@@ -2107,7 +2107,7 @@ export class CrossReviewOrchestrator {
|
|
|
2107
2107
|
},
|
|
2108
2108
|
});
|
|
2109
2109
|
peers.push(peerResult);
|
|
2110
|
-
this.store.savePeerResult(session.session_id, roundNumber, peerResult);
|
|
2110
|
+
await this.store.savePeerResult(session.session_id, roundNumber, peerResult);
|
|
2111
2111
|
continue;
|
|
2112
2112
|
}
|
|
2113
2113
|
const recovered = await adapter.call(recoveryPrompt, {
|
|
@@ -2140,25 +2140,25 @@ export class CrossReviewOrchestrator {
|
|
|
2140
2140
|
if (peerResult.status == null) {
|
|
2141
2141
|
const failure = unparseableAfterRecoveryFailure(peerResult);
|
|
2142
2142
|
rejected.push(failure);
|
|
2143
|
-
this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2143
|
+
await this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2144
2144
|
}
|
|
2145
2145
|
}
|
|
2146
2146
|
catch (error) {
|
|
2147
2147
|
const failure = classifyProviderError(adapter.id, adapter.provider, adapter.model, error, this.config.retry.max_attempts, Date.parse(startedAt));
|
|
2148
2148
|
rejected.push(failure);
|
|
2149
|
-
this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2149
|
+
await this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2150
2150
|
}
|
|
2151
2151
|
}
|
|
2152
2152
|
peers.push(peerResult);
|
|
2153
|
-
this.store.savePeerResult(session.session_id, roundNumber, peerResult);
|
|
2153
|
+
await this.store.savePeerResult(session.session_id, roundNumber, peerResult);
|
|
2154
2154
|
// v2.21.0 (caching): emit telemetry + persist manifest entry
|
|
2155
2155
|
// when the peer call surfaced any cache activity. Best-effort —
|
|
2156
2156
|
// failures here must not break the orchestrator critical path.
|
|
2157
|
-
this.recordCacheTelemetry(session.session_id, roundNumber, peerResult);
|
|
2157
|
+
await this.recordCacheTelemetry(session.session_id, roundNumber, peerResult);
|
|
2158
2158
|
if (peerResult.model_match === false) {
|
|
2159
2159
|
const failure = silentModelDowngradeFailure(peerResult);
|
|
2160
2160
|
rejected.push(failure);
|
|
2161
|
-
this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2161
|
+
await this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2162
2162
|
}
|
|
2163
2163
|
}
|
|
2164
2164
|
else if (item.failure) {
|
|
@@ -2170,7 +2170,7 @@ export class CrossReviewOrchestrator {
|
|
|
2170
2170
|
// badly, or a policy/budget/content stop, stays in `rejected`.
|
|
2171
2171
|
if (isSkippableFailure(failure)) {
|
|
2172
2172
|
skipped.push(failure);
|
|
2173
|
-
this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2173
|
+
await this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2174
2174
|
this.emit({
|
|
2175
2175
|
type: "session.peer_skipped_unavailable",
|
|
2176
2176
|
session_id: session.session_id,
|
|
@@ -2187,7 +2187,7 @@ export class CrossReviewOrchestrator {
|
|
|
2187
2187
|
}
|
|
2188
2188
|
else {
|
|
2189
2189
|
rejected.push(failure);
|
|
2190
|
-
this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2190
|
+
await this.store.savePeerFailure(session.session_id, roundNumber, failure);
|
|
2191
2191
|
}
|
|
2192
2192
|
}
|
|
2193
2193
|
}
|
|
@@ -2208,7 +2208,7 @@ export class CrossReviewOrchestrator {
|
|
|
2208
2208
|
recovery_converged: isRecoveryRound && quorumConvergence.converged,
|
|
2209
2209
|
quorum_peers: quorumPeers,
|
|
2210
2210
|
};
|
|
2211
|
-
const round = this.store.appendRound(session.session_id, {
|
|
2211
|
+
const round = await this.store.appendRound(session.session_id, {
|
|
2212
2212
|
caller_status: callerStatus,
|
|
2213
2213
|
draft_file: draftFile,
|
|
2214
2214
|
prompt_file: promptFile,
|
|
@@ -2227,7 +2227,7 @@ export class CrossReviewOrchestrator {
|
|
|
2227
2227
|
// v2.22.0 (B.P3): emit `session.budget_warning` if cumulative cost
|
|
2228
2228
|
// crossed 75% of the session ceiling on this round. One-shot;
|
|
2229
2229
|
// subsequent rounds in the same session won't re-emit.
|
|
2230
|
-
this.checkBudgetWarning(session.session_id, round.round);
|
|
2230
|
+
await this.checkBudgetWarning(session.session_id, round.round);
|
|
2231
2231
|
// v2.7.0 Evidence Broker: aggregate NEEDS_EVIDENCE asks from this
|
|
2232
2232
|
// round into the session-level checklist. Each peer that returned
|
|
2233
2233
|
// NEEDS_EVIDENCE with `caller_requests` contributes its asks; the
|
|
@@ -2244,7 +2244,7 @@ export class CrossReviewOrchestrator {
|
|
|
2244
2244
|
}
|
|
2245
2245
|
}
|
|
2246
2246
|
if (evidenceAsks.length > 0) {
|
|
2247
|
-
const checklist = this.store.appendEvidenceChecklistItems(session.session_id, round.round, evidenceAsks);
|
|
2247
|
+
const checklist = await this.store.appendEvidenceChecklistItems(session.session_id, round.round, evidenceAsks);
|
|
2248
2248
|
this.emit({
|
|
2249
2249
|
type: "session.evidence_checklist_updated",
|
|
2250
2250
|
session_id: session.session_id,
|
|
@@ -2266,7 +2266,7 @@ export class CrossReviewOrchestrator {
|
|
|
2266
2266
|
// to addressed. Skipping the call when evidenceAsks is empty would
|
|
2267
2267
|
// miss exactly the case the inference is designed for.
|
|
2268
2268
|
if ((this.store.read(session.session_id).evidence_checklist ?? []).length > 0) {
|
|
2269
|
-
const addressDetection = this.store.runEvidenceChecklistAddressDetection(session.session_id, round.round);
|
|
2269
|
+
const addressDetection = await this.store.runEvidenceChecklistAddressDetection(session.session_id, round.round);
|
|
2270
2270
|
if (addressDetection.not_resurfaced.length > 0) {
|
|
2271
2271
|
// v3.5.0 (CRV2-2): event renamed + message corrected. The prior
|
|
2272
2272
|
// `session.evidence_checklist_addressed` falsely implied the
|
|
@@ -2442,7 +2442,7 @@ export class CrossReviewOrchestrator {
|
|
|
2442
2442
|
let updated = this.store.read(session.session_id);
|
|
2443
2443
|
if (convergence.converged) {
|
|
2444
2444
|
this.store.saveFinal(session.session_id, input.draft);
|
|
2445
|
-
updated = this.store.finalize(session.session_id, "converged", convergence.recovery_converged ? "recovered_unanimity" : "unanimous_ready");
|
|
2445
|
+
updated = await this.store.finalize(session.session_id, "converged", convergence.recovery_converged ? "recovered_unanimity" : "unanimous_ready");
|
|
2446
2446
|
}
|
|
2447
2447
|
this.store.saveReport(session.session_id, sessionReportMarkdown(this.store.read(session.session_id), this.store.readEvents(session.session_id)));
|
|
2448
2448
|
this.emit({
|
|
@@ -2482,7 +2482,7 @@ export class CrossReviewOrchestrator {
|
|
|
2482
2482
|
// no-self-immediate-output invariant: between any peer's turn and
|
|
2483
2483
|
// their next turn, at least one different peer must hold custody.
|
|
2484
2484
|
if (sessionPeers.length < 2) {
|
|
2485
|
-
this.store.finalize(session.session_id, "aborted", "circular_rotation_too_small");
|
|
2485
|
+
await this.store.finalize(session.session_id, "aborted", "circular_rotation_too_small");
|
|
2486
2486
|
this.emit({
|
|
2487
2487
|
type: "session.circular_rotation_too_small",
|
|
2488
2488
|
session_id: session.session_id,
|
|
@@ -2512,7 +2512,7 @@ export class CrossReviewOrchestrator {
|
|
|
2512
2512
|
let consecutiveNoChangeCount = 0;
|
|
2513
2513
|
let lastRevisionRound = null;
|
|
2514
2514
|
let cursor = 0;
|
|
2515
|
-
this.store.setCircularState(session.session_id, {
|
|
2515
|
+
await this.store.setCircularState(session.session_id, {
|
|
2516
2516
|
rotation_order: rotationOrder,
|
|
2517
2517
|
consecutive_no_change_count: 0,
|
|
2518
2518
|
last_revision_round: null,
|
|
@@ -2534,7 +2534,7 @@ export class CrossReviewOrchestrator {
|
|
|
2534
2534
|
// no-self-immediate-output across the initial-draft → round 1 hop.
|
|
2535
2535
|
if (!draft) {
|
|
2536
2536
|
if (this.isCancelled(session.session_id, input.signal)) {
|
|
2537
|
-
this.store.markCancelled(session.session_id, "session_cancelled");
|
|
2537
|
+
await this.store.markCancelled(session.session_id, "session_cancelled");
|
|
2538
2538
|
return {
|
|
2539
2539
|
session: this.store.read(session.session_id),
|
|
2540
2540
|
final_text: draft,
|
|
@@ -2554,7 +2554,7 @@ export class CrossReviewOrchestrator {
|
|
|
2554
2554
|
reasoning_effort_override: input.reasoning_effort_overrides?.[initRotator],
|
|
2555
2555
|
caller: callerForLottery,
|
|
2556
2556
|
});
|
|
2557
|
-
this.store.saveGeneration(session.session_id, 0, initGeneration, "initial-draft");
|
|
2557
|
+
await this.store.saveGeneration(session.session_id, 0, initGeneration, "initial-draft");
|
|
2558
2558
|
if (detectLeadDrift(initGeneration.text) || initGeneration.text.trim() === "") {
|
|
2559
2559
|
this.emit({
|
|
2560
2560
|
type: "session.lead_drift_detected",
|
|
@@ -2569,7 +2569,7 @@ export class CrossReviewOrchestrator {
|
|
|
2569
2569
|
first_chars: initGeneration.text.slice(0, 100),
|
|
2570
2570
|
},
|
|
2571
2571
|
});
|
|
2572
|
-
this.store.finalize(session.session_id, "aborted", "lead_meta_review_drift");
|
|
2572
|
+
await this.store.finalize(session.session_id, "aborted", "lead_meta_review_drift");
|
|
2573
2573
|
return {
|
|
2574
2574
|
session: this.store.read(session.session_id),
|
|
2575
2575
|
final_text: undefined,
|
|
@@ -2591,7 +2591,7 @@ export class CrossReviewOrchestrator {
|
|
|
2591
2591
|
: circularMaxRotations * rotationOrder.length;
|
|
2592
2592
|
for (let round = 1; round <= maxCircularRounds; round++) {
|
|
2593
2593
|
if (this.isCancelled(session.session_id, input.signal)) {
|
|
2594
|
-
this.store.markCancelled(session.session_id, "session_cancelled");
|
|
2594
|
+
await this.store.markCancelled(session.session_id, "session_cancelled");
|
|
2595
2595
|
return {
|
|
2596
2596
|
session: this.store.read(session.session_id),
|
|
2597
2597
|
final_text: draft,
|
|
@@ -2600,7 +2600,7 @@ export class CrossReviewOrchestrator {
|
|
|
2600
2600
|
};
|
|
2601
2601
|
}
|
|
2602
2602
|
if (budgetExceeded(session, costLimit)) {
|
|
2603
|
-
this.store.finalize(session.session_id, "max-rounds", "budget_exceeded");
|
|
2603
|
+
await this.store.finalize(session.session_id, "max-rounds", "budget_exceeded");
|
|
2604
2604
|
this.emit({
|
|
2605
2605
|
type: "session.budget_exceeded",
|
|
2606
2606
|
session_id: session.session_id,
|
|
@@ -2630,7 +2630,7 @@ export class CrossReviewOrchestrator {
|
|
|
2630
2630
|
reasoning_effort_override: input.reasoning_effort_overrides?.[rotator],
|
|
2631
2631
|
caller: callerForLottery,
|
|
2632
2632
|
});
|
|
2633
|
-
this.store.saveGeneration(session.session_id, round, generation, "rotation");
|
|
2633
|
+
await this.store.saveGeneration(session.session_id, round, generation, "rotation");
|
|
2634
2634
|
// Drift / empty / fabrication detection — identical contract to
|
|
2635
2635
|
// ship mode's relator-revision branch. Two consecutive trips abort.
|
|
2636
2636
|
const emptyText = generation.text.trim() === "";
|
|
@@ -2692,7 +2692,7 @@ export class CrossReviewOrchestrator {
|
|
|
2692
2692
|
: fabricationDetected
|
|
2693
2693
|
? "lead_fabrication_repeated"
|
|
2694
2694
|
: "lead_meta_review_drift";
|
|
2695
|
-
this.store.finalize(session.session_id, "aborted", finalizeReason);
|
|
2695
|
+
await this.store.finalize(session.session_id, "aborted", finalizeReason);
|
|
2696
2696
|
return {
|
|
2697
2697
|
session: this.store.read(session.session_id),
|
|
2698
2698
|
final_text: draft,
|
|
@@ -2777,7 +2777,7 @@ export class CrossReviewOrchestrator {
|
|
|
2777
2777
|
reviewer_peers: rotationOrder,
|
|
2778
2778
|
lead_peer: rotator,
|
|
2779
2779
|
};
|
|
2780
|
-
this.store.appendRound(session.session_id, {
|
|
2780
|
+
await this.store.appendRound(session.session_id, {
|
|
2781
2781
|
caller_status: "READY",
|
|
2782
2782
|
prompt_file: promptFile,
|
|
2783
2783
|
peers: [peerResult],
|
|
@@ -2786,7 +2786,7 @@ export class CrossReviewOrchestrator {
|
|
|
2786
2786
|
convergence_scope: convergenceScope,
|
|
2787
2787
|
started_at: startedAt,
|
|
2788
2788
|
});
|
|
2789
|
-
this.store.setCircularState(session.session_id, {
|
|
2789
|
+
await this.store.setCircularState(session.session_id, {
|
|
2790
2790
|
rotation_order: rotationOrder,
|
|
2791
2791
|
consecutive_no_change_count: consecutiveNoChangeCount,
|
|
2792
2792
|
last_revision_round: lastRevisionRound,
|
|
@@ -2820,7 +2820,7 @@ export class CrossReviewOrchestrator {
|
|
|
2820
2820
|
last_revision_round: lastRevisionRound,
|
|
2821
2821
|
},
|
|
2822
2822
|
});
|
|
2823
|
-
this.store.finalize(session.session_id, "converged", "circular_full_rotation_no_change");
|
|
2823
|
+
await this.store.finalize(session.session_id, "converged", "circular_full_rotation_no_change");
|
|
2824
2824
|
return {
|
|
2825
2825
|
session: this.store.read(session.session_id),
|
|
2826
2826
|
final_text: draft,
|
|
@@ -2831,7 +2831,7 @@ export class CrossReviewOrchestrator {
|
|
|
2831
2831
|
cursor = (cursor + 1) % rotationOrder.length;
|
|
2832
2832
|
}
|
|
2833
2833
|
// Exhausted max rotations without convergence.
|
|
2834
|
-
this.store.finalize(session.session_id, "max-rounds", "circular_max_rotations_exceeded");
|
|
2834
|
+
await this.store.finalize(session.session_id, "max-rounds", "circular_max_rotations_exceeded");
|
|
2835
2835
|
this.emit({
|
|
2836
2836
|
type: "session.circular_max_rotations_exceeded",
|
|
2837
2837
|
session_id: session.session_id,
|
|
@@ -2998,8 +2998,8 @@ export class CrossReviewOrchestrator {
|
|
|
2998
2998
|
});
|
|
2999
2999
|
if (missingFinancialVars.length) {
|
|
3000
3000
|
const blockedSession = existingSession ??
|
|
3001
|
-
this.store.init(input.task, callerForLottery, [], normalizeReviewFocus(input.review_focus, this.config));
|
|
3002
|
-
this.store.finalize(blockedSession.session_id, "max-rounds", "financial_controls_missing");
|
|
3001
|
+
(await this.store.init(input.task, callerForLottery, [], normalizeReviewFocus(input.review_focus, this.config)));
|
|
3002
|
+
await this.store.finalize(blockedSession.session_id, "max-rounds", "financial_controls_missing");
|
|
3003
3003
|
this.emit({
|
|
3004
3004
|
type: "session.blocked.financial_controls_missing",
|
|
3005
3005
|
session_id: blockedSession.session_id,
|
|
@@ -3019,7 +3019,7 @@ export class CrossReviewOrchestrator {
|
|
|
3019
3019
|
let draft = input.initial_draft;
|
|
3020
3020
|
// v3.5.0 (CRV2-1 + CRV2-6): persist requested-vs-effective budget +
|
|
3021
3021
|
// max_rounds traceability once, before any round runs.
|
|
3022
|
-
this.store.setSessionTraceability(session.session_id, {
|
|
3022
|
+
await this.store.setSessionTraceability(session.session_id, {
|
|
3023
3023
|
requested_max_rounds: input.max_rounds ?? null,
|
|
3024
3024
|
effective_max_rounds: input.until_stopped ? null : effectiveMaxRounds,
|
|
3025
3025
|
requested_max_cost_usd: input.max_cost_usd ?? null,
|
|
@@ -3041,7 +3041,7 @@ export class CrossReviewOrchestrator {
|
|
|
3041
3041
|
attachmentsPresent,
|
|
3042
3042
|
});
|
|
3043
3043
|
if (!preflight.pass) {
|
|
3044
|
-
this.store.finalize(session.session_id, "aborted", "needs_evidence_preflight");
|
|
3044
|
+
await this.store.finalize(session.session_id, "aborted", "needs_evidence_preflight");
|
|
3045
3045
|
this.emit({
|
|
3046
3046
|
type: "session.evidence_preflight_failed",
|
|
3047
3047
|
session_id: session.session_id,
|
|
@@ -3065,7 +3065,7 @@ export class CrossReviewOrchestrator {
|
|
|
3065
3065
|
if (this.config.budget.require_rates_for_budget && costLimit != null) {
|
|
3066
3066
|
const missingRates = selectedPeers.filter((peer) => !this.config.cost_rates[peer]);
|
|
3067
3067
|
if (missingRates.length) {
|
|
3068
|
-
this.store.finalize(session.session_id, "max-rounds", "budget_requires_rates");
|
|
3068
|
+
await this.store.finalize(session.session_id, "max-rounds", "budget_requires_rates");
|
|
3069
3069
|
this.emit({
|
|
3070
3070
|
type: "session.blocked.budget_requires_rates",
|
|
3071
3071
|
session_id: session.session_id,
|
|
@@ -3102,7 +3102,7 @@ export class CrossReviewOrchestrator {
|
|
|
3102
3102
|
let consecutiveLeadDrifts = 0;
|
|
3103
3103
|
if (!draft) {
|
|
3104
3104
|
if (this.isCancelled(session.session_id, input.signal)) {
|
|
3105
|
-
this.store.markCancelled(session.session_id, "session_cancelled");
|
|
3105
|
+
await this.store.markCancelled(session.session_id, "session_cancelled");
|
|
3106
3106
|
return {
|
|
3107
3107
|
session: this.store.read(session.session_id),
|
|
3108
3108
|
converged: false,
|
|
@@ -3120,7 +3120,7 @@ export class CrossReviewOrchestrator {
|
|
|
3120
3120
|
reasoning_effort_override: input.reasoning_effort_overrides?.[leadPeer],
|
|
3121
3121
|
caller: callerForLottery,
|
|
3122
3122
|
});
|
|
3123
|
-
this.store.saveGeneration(session.session_id, 0, generation, "initial-draft");
|
|
3123
|
+
await this.store.saveGeneration(session.session_id, 0, generation, "initial-draft");
|
|
3124
3124
|
// v2.13.0: drift detection on initial-draft path. There is no
|
|
3125
3125
|
// prior draft to fall back to here, so a drifted initial generation
|
|
3126
3126
|
// aborts immediately. Only fires in `ship` mode — in `review` mode
|
|
@@ -3138,7 +3138,7 @@ export class CrossReviewOrchestrator {
|
|
|
3138
3138
|
first_chars: generation.text.slice(0, 100),
|
|
3139
3139
|
},
|
|
3140
3140
|
});
|
|
3141
|
-
this.store.finalize(session.session_id, "aborted", "lead_meta_review_drift");
|
|
3141
|
+
await this.store.finalize(session.session_id, "aborted", "lead_meta_review_drift");
|
|
3142
3142
|
return {
|
|
3143
3143
|
session: this.store.read(session.session_id),
|
|
3144
3144
|
final_text: undefined,
|
|
@@ -3150,7 +3150,7 @@ export class CrossReviewOrchestrator {
|
|
|
3150
3150
|
}
|
|
3151
3151
|
for (let round = 1; round <= effectiveMaxRounds; round++) {
|
|
3152
3152
|
if (this.isCancelled(session.session_id, input.signal)) {
|
|
3153
|
-
this.store.markCancelled(session.session_id, "session_cancelled");
|
|
3153
|
+
await this.store.markCancelled(session.session_id, "session_cancelled");
|
|
3154
3154
|
return {
|
|
3155
3155
|
session: this.store.read(session.session_id),
|
|
3156
3156
|
final_text: draft,
|
|
@@ -3181,7 +3181,7 @@ export class CrossReviewOrchestrator {
|
|
|
3181
3181
|
};
|
|
3182
3182
|
}
|
|
3183
3183
|
if (budgetExceeded(session, costLimit)) {
|
|
3184
|
-
this.store.finalize(session.session_id, "max-rounds", "budget_exceeded");
|
|
3184
|
+
await this.store.finalize(session.session_id, "max-rounds", "budget_exceeded");
|
|
3185
3185
|
return {
|
|
3186
3186
|
session: this.store.read(session.session_id),
|
|
3187
3187
|
final_text: draft,
|
|
@@ -3247,7 +3247,7 @@ export class CrossReviewOrchestrator {
|
|
|
3247
3247
|
reasoning_effort_override: input.reasoning_effort_overrides?.[leadPeer],
|
|
3248
3248
|
caller: callerForLottery,
|
|
3249
3249
|
});
|
|
3250
|
-
this.store.saveGeneration(session.session_id, round, generation, "revision");
|
|
3250
|
+
await this.store.saveGeneration(session.session_id, round, generation, "revision");
|
|
3251
3251
|
// v2.23.0: empty-text degeneracy detection. Provider-side parser
|
|
3252
3252
|
// diagnostics (e.g. Anthropic extended-thinking returning only
|
|
3253
3253
|
// `thinking`/`redacted_thinking` blocks with no final `text` block,
|
|
@@ -3402,7 +3402,7 @@ export class CrossReviewOrchestrator {
|
|
|
3402
3402
|
finalizeReason = "lead_meta_audit_repeated";
|
|
3403
3403
|
else
|
|
3404
3404
|
finalizeReason = "lead_meta_review_drift";
|
|
3405
|
-
this.store.finalize(session.session_id, "aborted", finalizeReason);
|
|
3405
|
+
await this.store.finalize(session.session_id, "aborted", finalizeReason);
|
|
3406
3406
|
return {
|
|
3407
3407
|
session: this.store.read(session.session_id),
|
|
3408
3408
|
final_text: draft,
|
|
@@ -3418,7 +3418,7 @@ export class CrossReviewOrchestrator {
|
|
|
3418
3418
|
}
|
|
3419
3419
|
}
|
|
3420
3420
|
}
|
|
3421
|
-
this.store.finalize(session.session_id, "max-rounds", "max_rounds_without_unanimity");
|
|
3421
|
+
await this.store.finalize(session.session_id, "max-rounds", "max_rounds_without_unanimity");
|
|
3422
3422
|
return {
|
|
3423
3423
|
session: this.store.read(session.session_id),
|
|
3424
3424
|
final_text: draft,
|