@nexus-cortex/core 4.92.0 → 4.94.0
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/{.env.example → .env.defaults} +93 -25
- package/dist/config/MentorshipConfigService.d.ts +0 -1
- package/dist/config/MentorshipConfigService.d.ts.map +1 -1
- package/dist/config/MentorshipConfigService.js +10 -5
- package/dist/config/MentorshipConfigService.js.map +1 -1
- package/dist/config/SettingsLoader.d.ts +92 -11
- package/dist/config/SettingsLoader.d.ts.map +1 -1
- package/dist/config/SettingsLoader.js +331 -131
- package/dist/config/SettingsLoader.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts +1 -1
- package/dist/config/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +6 -1
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/middleware/HelperModelMiddleware.d.ts +16 -0
- package/dist/middleware/HelperModelMiddleware.d.ts.map +1 -1
- package/dist/middleware/HelperModelMiddleware.js +32 -5
- package/dist/middleware/HelperModelMiddleware.js.map +1 -1
- package/dist/models/cards/deepseek/deepseek-v4-flash.d.ts.map +1 -1
- package/dist/models/cards/deepseek/deepseek-v4-flash.js +4 -2
- package/dist/models/cards/deepseek/deepseek-v4-flash.js.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.d.ts +14 -0
- package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.js +148 -7
- package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
- package/dist/orchestrator/OrchestratorFactory.js +1 -1
- package/dist/orchestrator/OrchestratorFactory.js.map +1 -1
- package/dist/orchestrator/displayHistorySanitizer.d.ts +29 -0
- package/dist/orchestrator/displayHistorySanitizer.d.ts.map +1 -0
- package/dist/orchestrator/displayHistorySanitizer.js +62 -0
- package/dist/orchestrator/displayHistorySanitizer.js.map +1 -0
- package/dist/tools/context-management/InitCortexContext.js +1 -1
- package/dist/tools/context-management/InitCortexContext.js.map +1 -1
- package/dist/training/DecisionStore.d.ts +1 -1
- package/dist/training/DecisionStore.d.ts.map +1 -1
- package/dist/training/DecisionStore.js.map +1 -1
- package/dist/training/deadlineExitMentor.d.ts +67 -0
- package/dist/training/deadlineExitMentor.d.ts.map +1 -0
- package/dist/training/deadlineExitMentor.js +99 -0
- package/dist/training/deadlineExitMentor.js.map +1 -0
- package/dist/training/endTurnResolver.d.ts +14 -3
- package/dist/training/endTurnResolver.d.ts.map +1 -1
- package/dist/training/endTurnResolver.js +27 -10
- package/dist/training/endTurnResolver.js.map +1 -1
- package/package.json +5 -5
|
@@ -12,6 +12,7 @@ import { ToolNamingHandler } from '../adapters/ToolNamingHandler.js';
|
|
|
12
12
|
// Phase 2.4: Server-Side Tools Support
|
|
13
13
|
import { shouldUseServerSideTools, isServerSideToolsEnabled, modelSupportsServerSideTools } from '../adapters/ServerSideToolDetection.js';
|
|
14
14
|
import { shouldAutoInjectMcp } from './mcpAutoInjectPolicy.js';
|
|
15
|
+
import { sanitizeHistoryForDisplay } from './displayHistorySanitizer.js';
|
|
15
16
|
import { verifyCoordinates, deterministicCoordinateScore } from './coordinateVerification.js';
|
|
16
17
|
import { buildRouterSample, appendJsonlRotating } from './cortexTrainingRecord.js';
|
|
17
18
|
import { join as pathJoin } from 'path';
|
|
@@ -19,6 +20,7 @@ import { existsSync } from 'fs';
|
|
|
19
20
|
import { execSync } from 'node:child_process';
|
|
20
21
|
import { ENV_RECON_COMMAND, resolveLiftPlanConfig } from '../training/liftPlanner.js';
|
|
21
22
|
import { resolveEndTurnResolverConfig, parseResolverVerdict } from '../training/endTurnResolver.js';
|
|
23
|
+
import { resolveDeadlineExitConfig, deadlineExitCallBudget, parseDeadlineExitVerdict } from '../training/deadlineExitMentor.js';
|
|
22
24
|
import { isTaskShaped } from './requirementsVerification.js';
|
|
23
25
|
import { extractServerSideMetadata, XAIServerSideTools, OpenAIServerSideTools, toCanonicalTool } from '../tools/ServerSideTools.js';
|
|
24
26
|
// Phase 2.2.1: Timeline and History Storage
|
|
@@ -394,12 +396,14 @@ export class CortexOrchestrator {
|
|
|
394
396
|
// timeout/non-zero is still useful; empty on total failure → plan from observations alone).
|
|
395
397
|
const envReport = this.gatherEnvReport();
|
|
396
398
|
const timeoutMs = parseInt(process.env.CORTEX_LIFT_PLAN_TIMEOUT_MS ?? '90000', 10);
|
|
399
|
+
const t0 = Date.now();
|
|
397
400
|
const plan = await withTimeout(this.helperMiddleware.generateTaskPlan({
|
|
398
401
|
task: this.lastRealUserText(),
|
|
399
402
|
observations,
|
|
400
403
|
envReport,
|
|
401
404
|
helperModelId: this.config.reactiveMentorship?.helperModelId,
|
|
402
405
|
}), timeoutMs);
|
|
406
|
+
const latencyMs = Date.now() - t0;
|
|
403
407
|
if (!plan || !plan.trim()) {
|
|
404
408
|
if (store)
|
|
405
409
|
void store.recordEvent({
|
|
@@ -419,7 +423,9 @@ export class CortexOrchestrator {
|
|
|
419
423
|
void store.recordEvent({
|
|
420
424
|
sessionId,
|
|
421
425
|
kind: 'lift_plan',
|
|
422
|
-
|
|
426
|
+
// OBSERVABILITY (resolver-AB follow-up): bank the plan TEXT + latency, not just counts —
|
|
427
|
+
// so a k=5 run can score plan QUALITY (and read the reasoning behind a RETIRE).
|
|
428
|
+
detail: { fired: true, planChars: plan.length, retire, criteriaStated, latencyMs, planText: plan.slice(0, 4000) },
|
|
423
429
|
}).catch(() => { });
|
|
424
430
|
if (this.config.debug) {
|
|
425
431
|
console.log(`[LiftPlan] plan delivered at lift (${plan.length} chars, retire=${retire}, criteria=${criteriaStated})`);
|
|
@@ -505,6 +511,7 @@ export class CortexOrchestrator {
|
|
|
505
511
|
const workProduct = `${this.lastAssistantText()}\n\n--- checks / tool outputs this task ---\n${outputs}`.slice(0, 8000);
|
|
506
512
|
const attestation = JSON.stringify(etInput).slice(0, 2000);
|
|
507
513
|
const timeoutMs = parseInt(process.env.CORTEX_ENDTURN_RESOLVER_TIMEOUT_MS ?? '90000', 10);
|
|
514
|
+
const t0 = Date.now();
|
|
508
515
|
const text = await withTimeout(this.helperMiddleware.evaluateEndTurn({
|
|
509
516
|
task,
|
|
510
517
|
envReport: this.gatherEnvReport(),
|
|
@@ -512,15 +519,34 @@ export class CortexOrchestrator {
|
|
|
512
519
|
attestation,
|
|
513
520
|
helperModelId: this.config.reactiveMentorship?.helperModelId,
|
|
514
521
|
}), timeoutMs);
|
|
522
|
+
const latencyMs = Date.now() - t0;
|
|
515
523
|
const verdict = parseResolverVerdict(text ?? ''); // withTimeout → null on timeout; fail-open to MEETS
|
|
524
|
+
// OBSERVABILITY (resolver-AB follow-up, 2026-09-05): bank the TEXT the resolver produced +
|
|
525
|
+
// what it judged, not just counts — so a k=5 run can score the resolver's JUDGMENT QUALITY
|
|
526
|
+
// (was the GAP warranted? was the fix plan good? did it judge the right artifact?) and not
|
|
527
|
+
// merely that it fired. Bounded caps keep the decisions row sane. See HARNESS_IMPROVEMENT_BACKLOG.
|
|
516
528
|
if (store)
|
|
517
529
|
void store.recordEvent({
|
|
518
530
|
sessionId,
|
|
519
531
|
kind: 'endturn_resolver',
|
|
520
532
|
toolName: 'EndTurn',
|
|
521
|
-
detail: {
|
|
533
|
+
detail: {
|
|
534
|
+
meets: verdict.meets, retire: verdict.retire, abstained: verdict.retire && cfg.abstain,
|
|
535
|
+
planChars: verdict.plan.length, rejects: this.endTurnResolverRejects, parsed: verdict.parsed,
|
|
536
|
+
latencyMs, rawLen: (text ?? '').length,
|
|
537
|
+
planText: verdict.plan.slice(0, 4000),
|
|
538
|
+
workProductSample: workProduct.slice(0, 1500),
|
|
539
|
+
attestation: attestation.slice(0, 800),
|
|
540
|
+
},
|
|
522
541
|
}).catch(() => { });
|
|
523
|
-
if (
|
|
542
|
+
if (verdict.retire && cfg.abstain) {
|
|
543
|
+
// ABSTENTION — the finish is structurally unclosable; accept it and STOP the reject
|
|
544
|
+
// loop (no veto, no reject increment) instead of burning more pro@max cycles on a task
|
|
545
|
+
// the junior can't fix. The finish stands (ev.endTurnCalled stays true).
|
|
546
|
+
if (this.config.debug)
|
|
547
|
+
console.warn(`[EndTurnResolver] RETIRE — abstained (unclosable): ${verdict.plan.slice(0, 160)}`);
|
|
548
|
+
}
|
|
549
|
+
else if (!verdict.meets && verdict.plan) {
|
|
524
550
|
this.endTurnResolverRejects += 1;
|
|
525
551
|
ev.endTurnCalled = false; // VETO the finish
|
|
526
552
|
et.is_error = true;
|
|
@@ -540,6 +566,78 @@ export class CortexOrchestrator {
|
|
|
540
566
|
// fail-open: leave the finish accepted
|
|
541
567
|
}
|
|
542
568
|
}
|
|
569
|
+
/**
|
|
570
|
+
* deadlineExitCheckpoint — the mentor-as-deadline-checkpoint at the WARN rung (~0.9 of the
|
|
571
|
+
* per-turn deadline, where residual still exists). Returns null when disabled/not-applicable
|
|
572
|
+
* (caller uses its normal warn nudge). Otherwise a bounded, residual-scaled mentor call decides:
|
|
573
|
+
* shouldBreak=true → FINISH or RETIRE: end the loop now (post-loop synthesis; resolver bypassed
|
|
574
|
+
* via abnormal-exit) — a clean finish or a clean retirement instead of
|
|
575
|
+
* grinding the residual.
|
|
576
|
+
* shouldBreak=false → CONTINUE (signal='' → normal nudge) or ACTION (signal=the one directed step).
|
|
577
|
+
* Dark by default (CORTEX_DEADLINE_EXIT_MENTOR); fail-safe to null on any error (never truncate
|
|
578
|
+
* a turn on a broken checkpoint call).
|
|
579
|
+
*/
|
|
580
|
+
async deadlineExitCheckpoint(task, workProduct, remainingMs) {
|
|
581
|
+
const cfg = resolveDeadlineExitConfig();
|
|
582
|
+
if (!cfg.enabled)
|
|
583
|
+
return null;
|
|
584
|
+
if (!this.helperMiddleware?.evaluateDeadlineExit)
|
|
585
|
+
return null;
|
|
586
|
+
if (!isTaskShaped(task))
|
|
587
|
+
return null;
|
|
588
|
+
const { timeoutMs, outputBudgetTokens } = deadlineExitCallBudget(cfg, remainingMs);
|
|
589
|
+
const store = this.getDecisionStore();
|
|
590
|
+
const sessionId = this.currentSessionId ?? 'unknown';
|
|
591
|
+
try {
|
|
592
|
+
const t0 = Date.now();
|
|
593
|
+
const text = await withTimeout(this.helperMiddleware.evaluateDeadlineExit({
|
|
594
|
+
task,
|
|
595
|
+
envReport: this.gatherEnvReport(),
|
|
596
|
+
workProduct,
|
|
597
|
+
remainingBudget: `~${Math.max(0, Math.round(remainingMs / 1000))}s of budget left`,
|
|
598
|
+
outputBudgetTokens,
|
|
599
|
+
effort: cfg.effort,
|
|
600
|
+
helperModelId: this.config.reactiveMentorship?.helperModelId,
|
|
601
|
+
}), timeoutMs);
|
|
602
|
+
const v = parseDeadlineExitVerdict(text ?? '');
|
|
603
|
+
if (store)
|
|
604
|
+
void store.recordEvent({
|
|
605
|
+
sessionId,
|
|
606
|
+
kind: 'deadline_exit_mentor',
|
|
607
|
+
detail: {
|
|
608
|
+
decision: v.decision, parsed: v.parsed, remainingMs, latencyMs: Date.now() - t0,
|
|
609
|
+
detailText: v.detail.slice(0, 2000), workProductSample: workProduct.slice(0, 1200),
|
|
610
|
+
},
|
|
611
|
+
}).catch(() => { });
|
|
612
|
+
switch (v.decision) {
|
|
613
|
+
case 'finish':
|
|
614
|
+
if (this.config.debug)
|
|
615
|
+
console.warn(`[DeadlineExit] FINISH — mentor confirms criteria met; ending turn`);
|
|
616
|
+
return { shouldBreak: true, signal: '' };
|
|
617
|
+
case 'retire':
|
|
618
|
+
if (this.config.debug)
|
|
619
|
+
console.warn(`[DeadlineExit] RETIRE — unclosable in residual (${v.detail.slice(0, 120)}); ending turn`);
|
|
620
|
+
return { shouldBreak: true, signal: '' };
|
|
621
|
+
case 'action':
|
|
622
|
+
return { shouldBreak: false, signal: `Time is short — a reviewer says ONE action closes the gap:\n${v.detail}\nDo exactly that, verify it against the task's real criteria, then finish.` };
|
|
623
|
+
default:
|
|
624
|
+
return { shouldBreak: false, signal: '' }; // CONTINUE → caller uses the normal warn nudge
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
catch (e) {
|
|
628
|
+
if (store)
|
|
629
|
+
void store.recordEvent({ sessionId, kind: 'deadline_exit_mentor', detail: { error: String(e?.message ?? e).slice(0, 120) } }).catch(() => { });
|
|
630
|
+
return null; // fail-safe: fall back to the normal warn nudge
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
/** Build the bounded work-product snapshot for the deadline checkpoint (latest answer + recent tool outputs). */
|
|
634
|
+
buildDeadlineWorkProduct(toolResults) {
|
|
635
|
+
const outs = toolResults
|
|
636
|
+
.slice(-6)
|
|
637
|
+
.map((r) => `[${r.tool_name}${r.is_error ? ' ERR' : ''}] ${(typeof r.content === 'string' ? r.content : JSON.stringify(r.content ?? '')).slice(0, 500)}`)
|
|
638
|
+
.join('\n');
|
|
639
|
+
return `${this.lastAssistantText()}\n\n--- recent tool outputs ---\n${outs}`.slice(0, 8000);
|
|
640
|
+
}
|
|
543
641
|
/** Narrow `tools` to the anchor profile while the anchor is armed; no-op
|
|
544
642
|
* once lifted or when CORTEX_TOOL_ANCHOR is unset. Applied AFTER the
|
|
545
643
|
* deferred filter at both request-assembly sites. */
|
|
@@ -1370,6 +1468,16 @@ export class CortexOrchestrator {
|
|
|
1370
1468
|
let toolCallIteration = 0;
|
|
1371
1469
|
// Loop control settings — single source of truth via getLoopControlConfig()
|
|
1372
1470
|
const loopDefaults = this.getLoopControlConfig();
|
|
1471
|
+
// CORTEX_ACTION_EFFORT: static reasoning effort for the PRIMARY (action) model — fills the
|
|
1472
|
+
// request param when unset (`??=`), so it overrides the model card's default but NOT an
|
|
1473
|
+
// explicit request param or the dynamic effort-pulse (both still win). Mentor calls
|
|
1474
|
+
// (lift-plan / endturn-resolver / deadline-exit) use their own *_EFFORT and are unaffected.
|
|
1475
|
+
// Enables the pro-track "wide effort delta" A/B (pro@low action + pro@max mentors).
|
|
1476
|
+
const __actionEffort = (process.env.CORTEX_ACTION_EFFORT || '').trim();
|
|
1477
|
+
if (__actionEffort) {
|
|
1478
|
+
options.parameters = options.parameters || {};
|
|
1479
|
+
options.parameters.reasoningEffort ??= __actionEffort;
|
|
1480
|
+
}
|
|
1373
1481
|
const MAX_TOOL_ITERATIONS = loopDefaults.maxToolIterations;
|
|
1374
1482
|
const TOOL_TIMEOUT_MS = loopDefaults.toolTimeoutMs;
|
|
1375
1483
|
const MAX_CONSECUTIVE_ERRORS = loopDefaults.maxConsecutiveErrors;
|
|
@@ -2343,10 +2451,20 @@ export class CortexOrchestrator {
|
|
|
2343
2451
|
const budgetSignal = computeToolBudgetSignal(effectiveToolBudgetCount, TOOL_BUDGET_SOFT, progressStalled);
|
|
2344
2452
|
const diversityWarning = this.getDiversityWarning(toolCallCounts);
|
|
2345
2453
|
// #2: one-shot wall-clock warning at 90% of the deadline — tell the model to converge + EndTurn.
|
|
2454
|
+
// DARK deadline-exit-mentor (CORTEX_DEADLINE_EXIT_MENTOR): at the warn rung, a bounded mentor
|
|
2455
|
+
// may decide FINISH/RETIRE (end now, cleanly) or ACTION (one directed step) instead of the dumb
|
|
2456
|
+
// "wrap up" nudge; null/CONTINUE falls back to the normal nudge. Hard-floor break below stays.
|
|
2346
2457
|
let timeSignal = null;
|
|
2347
2458
|
if (!timeWarnFired && timeBudgetState(Date.now() - loopStartMs, TURN_DEADLINE_MS) === 'warn') {
|
|
2348
|
-
timeSignal = timeBudgetWarnNudge(Date.now() - loopStartMs, TURN_DEADLINE_MS);
|
|
2349
2459
|
timeWarnFired = true;
|
|
2460
|
+
const de = await this.deadlineExitCheckpoint(this.lastRealUserText(), this.buildDeadlineWorkProduct(toolResults), TURN_DEADLINE_MS - (Date.now() - loopStartMs));
|
|
2461
|
+
if (de?.shouldBreak) {
|
|
2462
|
+
const dstore = this.getDecisionStore();
|
|
2463
|
+
if (dstore)
|
|
2464
|
+
void dstore.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'deadline_exit_break', detail: { iteration: toolCallIteration } }).catch(() => { });
|
|
2465
|
+
break; // mentor-directed clean exit → post-loop synthesis (resolver bypassed via abnormal-exit)
|
|
2466
|
+
}
|
|
2467
|
+
timeSignal = de?.signal || timeBudgetWarnNudge(Date.now() - loopStartMs, TURN_DEADLINE_MS);
|
|
2350
2468
|
}
|
|
2351
2469
|
if (budgetSignal || diversityWarning || ladderSignal || timeSignal) {
|
|
2352
2470
|
const signals = [budgetSignal, diversityWarning, ladderSignal, timeSignal].filter(Boolean).join('\n');
|
|
@@ -3513,6 +3631,16 @@ export class CortexOrchestrator {
|
|
|
3513
3631
|
let hasToolUse = initialToolUseBlocks.length > 0;
|
|
3514
3632
|
// Loop control settings — single source of truth via getLoopControlConfig()
|
|
3515
3633
|
const loopDefaults = this.getLoopControlConfig();
|
|
3634
|
+
// CORTEX_ACTION_EFFORT: static reasoning effort for the PRIMARY (action) model — fills the
|
|
3635
|
+
// request param when unset (`??=`), so it overrides the model card's default but NOT an
|
|
3636
|
+
// explicit request param or the dynamic effort-pulse (both still win). Mentor calls
|
|
3637
|
+
// (lift-plan / endturn-resolver / deadline-exit) use their own *_EFFORT and are unaffected.
|
|
3638
|
+
// Enables the pro-track "wide effort delta" A/B (pro@low action + pro@max mentors).
|
|
3639
|
+
const __actionEffort = (process.env.CORTEX_ACTION_EFFORT || '').trim();
|
|
3640
|
+
if (__actionEffort) {
|
|
3641
|
+
options.parameters = options.parameters || {};
|
|
3642
|
+
options.parameters.reasoningEffort ??= __actionEffort;
|
|
3643
|
+
}
|
|
3516
3644
|
const MAX_TOOL_ITERATIONS = loopDefaults.maxToolIterations;
|
|
3517
3645
|
const MAX_CONSECUTIVE_ERRORS = loopDefaults.maxConsecutiveErrors;
|
|
3518
3646
|
const MAX_LOOP_REPETITIONS = loopDefaults.maxLoopRepetitions;
|
|
@@ -4048,11 +4176,19 @@ export class CortexOrchestrator {
|
|
|
4048
4176
|
const progressStalled = isToolProgressStalled(allToolCalls);
|
|
4049
4177
|
const budgetSignal = computeToolBudgetSignal(effectiveToolBudgetCount, TOOL_BUDGET_SOFT, progressStalled);
|
|
4050
4178
|
const diversityWarning = this.getDiversityWarning(toolCallCounts);
|
|
4051
|
-
// #2: one-shot wall-clock warning at 90% of the deadline (streaming parity).
|
|
4179
|
+
// #2: one-shot wall-clock warning at 90% of the deadline (streaming parity). DARK
|
|
4180
|
+
// deadline-exit-mentor may FINISH/RETIRE (end cleanly) or ACTION (one step) here.
|
|
4052
4181
|
let timeSignal = null;
|
|
4053
4182
|
if (!timeWarnFired && timeBudgetState(Date.now() - loopStartMs, TURN_DEADLINE_MS) === 'warn') {
|
|
4054
|
-
timeSignal = timeBudgetWarnNudge(Date.now() - loopStartMs, TURN_DEADLINE_MS);
|
|
4055
4183
|
timeWarnFired = true;
|
|
4184
|
+
const de = await this.deadlineExitCheckpoint(this.lastRealUserText(), this.buildDeadlineWorkProduct(toolResults), TURN_DEADLINE_MS - (Date.now() - loopStartMs));
|
|
4185
|
+
if (de?.shouldBreak) {
|
|
4186
|
+
const dstore = this.getDecisionStore();
|
|
4187
|
+
if (dstore)
|
|
4188
|
+
void dstore.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'deadline_exit_break', detail: { iteration: toolCallIteration } }).catch(() => { });
|
|
4189
|
+
break; // mentor-directed clean exit → post-loop synthesis (resolver bypassed via abnormal-exit)
|
|
4190
|
+
}
|
|
4191
|
+
timeSignal = de?.signal || timeBudgetWarnNudge(Date.now() - loopStartMs, TURN_DEADLINE_MS);
|
|
4056
4192
|
}
|
|
4057
4193
|
if (budgetSignal || diversityWarning || ladderSignal || timeSignal) {
|
|
4058
4194
|
const signals = [budgetSignal, diversityWarning, ladderSignal, timeSignal].filter(Boolean).join('\n');
|
|
@@ -4938,7 +5074,12 @@ export class CortexOrchestrator {
|
|
|
4938
5074
|
});
|
|
4939
5075
|
});
|
|
4940
5076
|
}
|
|
4941
|
-
|
|
5077
|
+
// Strip model-only <system-reminder> scaffolding (lift-nudge / deferred
|
|
5078
|
+
// corpus / lift-plan append these onto history messages) — this accessor
|
|
5079
|
+
// feeds the TUI history view + session export, never the model request, so
|
|
5080
|
+
// the reminders must not surface to a human. Returns sanitized copies; the
|
|
5081
|
+
// model-facing messageHistory objects are untouched. See displayHistorySanitizer.
|
|
5082
|
+
return sanitizeHistoryForDisplay([...this.messageHistory]);
|
|
4942
5083
|
}
|
|
4943
5084
|
/**
|
|
4944
5085
|
* Resume an existing session by loading its full history
|