@nexus-cortex/core 4.124.43 → 4.124.45
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.defaults +184 -0
- package/dist/adapters/GatewayTranslationLayer.d.ts.map +1 -1
- package/dist/adapters/GatewayTranslationLayer.js +14 -1
- package/dist/adapters/GatewayTranslationLayer.js.map +1 -1
- package/dist/adapters/ServerSideToolDetection.d.ts.map +1 -1
- package/dist/adapters/ServerSideToolDetection.js +5 -1
- package/dist/adapters/ServerSideToolDetection.js.map +1 -1
- package/dist/config/SettingsLoader.d.ts +3 -1
- package/dist/config/SettingsLoader.d.ts.map +1 -1
- package/dist/config/SettingsLoader.js +18 -4
- package/dist/config/SettingsLoader.js.map +1 -1
- package/dist/config/SettingsSchema.d.ts +1 -0
- package/dist/config/SettingsSchema.d.ts.map +1 -1
- package/dist/config/SettingsSchema.js +9 -0
- package/dist/config/SettingsSchema.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 +21 -1
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/config/interactiveProfile.d.ts +86 -0
- package/dist/config/interactiveProfile.d.ts.map +1 -0
- package/dist/config/interactiveProfile.js +114 -0
- package/dist/config/interactiveProfile.js.map +1 -0
- package/dist/middleware/HelperModelMiddleware.d.ts +10 -0
- package/dist/middleware/HelperModelMiddleware.d.ts.map +1 -1
- package/dist/middleware/HelperModelMiddleware.js +48 -5
- package/dist/middleware/HelperModelMiddleware.js.map +1 -1
- package/dist/middleware/SystemMessageMiddleware.d.ts.map +1 -1
- package/dist/middleware/SystemMessageMiddleware.js +4 -2
- package/dist/middleware/SystemMessageMiddleware.js.map +1 -1
- package/dist/middleware/helpers/helperProviderMatch.d.ts +40 -0
- package/dist/middleware/helpers/helperProviderMatch.d.ts.map +1 -0
- package/dist/middleware/helpers/helperProviderMatch.js +50 -0
- package/dist/middleware/helpers/helperProviderMatch.js.map +1 -0
- package/dist/orchestrator/APIClient.d.ts.map +1 -1
- package/dist/orchestrator/APIClient.js +92 -13
- package/dist/orchestrator/APIClient.js.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.d.ts +22 -0
- package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.js +211 -82
- package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
- package/dist/orchestrator/OrchestratorFactory.d.ts.map +1 -1
- package/dist/orchestrator/OrchestratorFactory.js +10 -0
- package/dist/orchestrator/OrchestratorFactory.js.map +1 -1
- package/dist/orchestrator/appendOnlyHistory.d.ts +39 -0
- package/dist/orchestrator/appendOnlyHistory.d.ts.map +1 -0
- package/dist/orchestrator/appendOnlyHistory.js +75 -0
- package/dist/orchestrator/appendOnlyHistory.js.map +1 -0
- package/dist/orchestrator/transportCacheFixes.d.ts +22 -0
- package/dist/orchestrator/transportCacheFixes.d.ts.map +1 -0
- package/dist/orchestrator/transportCacheFixes.js +114 -0
- package/dist/orchestrator/transportCacheFixes.js.map +1 -0
- package/dist/orchestrator/transportFixes.d.ts +55 -0
- package/dist/orchestrator/transportFixes.d.ts.map +1 -0
- package/dist/orchestrator/transportFixes.js +182 -0
- package/dist/orchestrator/transportFixes.js.map +1 -0
- package/dist/orchestrator/wallGuard.d.ts +3 -1
- package/dist/orchestrator/wallGuard.d.ts.map +1 -1
- package/dist/orchestrator/wallGuard.js +15 -2
- package/dist/orchestrator/wallGuard.js.map +1 -1
- package/dist/system-messages/promptPresets.d.ts +29 -1
- package/dist/system-messages/promptPresets.d.ts.map +1 -1
- package/dist/system-messages/promptPresets.js +44 -2
- package/dist/system-messages/promptPresets.js.map +1 -1
- package/dist/tools/ClientSideToolFilter.d.ts +9 -2
- package/dist/tools/ClientSideToolFilter.d.ts.map +1 -1
- package/dist/tools/ClientSideToolFilter.js +26 -3
- package/dist/tools/ClientSideToolFilter.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/package.json +3 -3
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Based on: docs/v4-core_library/Orchestrator_ARCHITECTURE.md
|
|
9
9
|
*/
|
|
10
|
+
import { liftPlanAllowedInSession } from '../config/interactiveProfile.js';
|
|
10
11
|
import { v4 as uuidv4 } from 'uuid';
|
|
11
12
|
import { ToolNamingHandler } from '../adapters/ToolNamingHandler.js';
|
|
12
13
|
// Phase 2.4: Server-Side Tools Support
|
|
@@ -90,6 +91,7 @@ import { InitCortexContext, MemoryWrite, MemoryRecall } from '../tools/context-m
|
|
|
90
91
|
import { pruneAgedToolResults } from '../conversation/ToolResultPruner.js';
|
|
91
92
|
import { detectTailRepetition, tailLoopGuardEnabled } from './tailRepetitionDetector.js';
|
|
92
93
|
import { resolveEffortRamp, rampEffortFor } from './effortRamp.js';
|
|
94
|
+
import { resolveWallCacheFix, resolvePersistInjected, resolveAppendOnlyTools, resolveKeepMentorMessages, resolveResponsesSliceAll, chainedSliceStart, tailUnitAfterLastAssistant, isPreviousResponseUnavailable } from './appendOnlyHistory.js'; // R221-R228 append-only history (dark)
|
|
93
95
|
import { resolveWallDrop, resolveOutputCap, dropWalledTurn, resolveWallSummary, extractWalledReasoning, clipReasoning, formatWallSummary } from './wallGuard.js';
|
|
94
96
|
import { resolvePid1Guard, detectPid1Note } from './pid1Guard.js'; // R209
|
|
95
97
|
import { PlateauStopTracker, plateauBootInstruction, plateauStatusSegment } from './plateauStop.js'; // R210
|
|
@@ -381,6 +383,7 @@ export class CortexOrchestrator {
|
|
|
381
383
|
type: 'text',
|
|
382
384
|
text: `<system-reminder>\nFull session context follows (deferred until your first action; applies from here on).\n</system-reminder>\n${corpus}`
|
|
383
385
|
});
|
|
386
|
+
this.invalidateTailUnitConversions(); // R221 safety net (CORTEX_WALL_CACHE_FIX; off = no-op)
|
|
384
387
|
if (this.config.debug) {
|
|
385
388
|
console.log(`[Anchor] deferred static corpus delivered at lift (${corpus.length} chars)`);
|
|
386
389
|
}
|
|
@@ -423,6 +426,7 @@ export class CortexOrchestrator {
|
|
|
423
426
|
if (!r)
|
|
424
427
|
return;
|
|
425
428
|
lastMsg.message.content.push({ type: 'text', text: `<system-reminder>\n${r.note}\n</system-reminder>` });
|
|
429
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
426
430
|
const store = this.getDecisionStore();
|
|
427
431
|
if (store)
|
|
428
432
|
void store.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'pid1_guard', detail: { noted: true, pid1Cmd: r.info.pid1Cmd, children: r.info.children, chars: r.note.length } }).catch(() => { });
|
|
@@ -446,6 +450,7 @@ export class CortexOrchestrator {
|
|
|
446
450
|
return; // not the boundary yet — leave the one-shot open
|
|
447
451
|
this.plateauInstructionDelivered = true;
|
|
448
452
|
lastMsg.message.content.push({ type: 'text', text: `<system-reminder>\n${text}\n</system-reminder>` });
|
|
453
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
449
454
|
if (this.config.debug)
|
|
450
455
|
console.log('[PlateauStop] boot instruction delivered');
|
|
451
456
|
}
|
|
@@ -520,6 +525,7 @@ export class CortexOrchestrator {
|
|
|
520
525
|
text: `<system-reminder>\n${parts.join(' ')}\n</system-reminder>`
|
|
521
526
|
});
|
|
522
527
|
this.liftNudgeDelivered = true; // one-shot: consumed only on ACTUAL delivery
|
|
528
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
523
529
|
if (this.config.debug) {
|
|
524
530
|
console.log(`[Anchor] lift nudge delivered (${parts.join(' ').length} chars, ${hidden.length} hidden tools)`);
|
|
525
531
|
}
|
|
@@ -550,7 +556,9 @@ export class CortexOrchestrator {
|
|
|
550
556
|
// there is no human watching — fine. In an interactive TUI a silent pause reads as a frozen UI,
|
|
551
557
|
// so the planner is SUPPRESSED there UNLESS a thinking-indicator is wired and opted in via
|
|
552
558
|
// CORTEX_LIFT_PLAN_INTERACTIVE=true. (autoApproveActions = !(stdin.isTTY && stdout.isTTY).)
|
|
553
|
-
|
|
559
|
+
// R219: the gate lives in config/interactiveProfile.ts (same env the CORTEX_INTERACTIVE_PROFILE resolver fills;
|
|
560
|
+
// bench-lite leaves it closed — NEEDS-ADAPTATION: silent pause, first-request-only, grader persona).
|
|
561
|
+
if (!liftPlanAllowedInSession(this.approvalMode.autoApproveActions)) {
|
|
554
562
|
return; // interactive session, no thinking-indicator opt-in → do not fire (do not consume one-shot)
|
|
555
563
|
}
|
|
556
564
|
this.liftPlanDelivered = true; // one-shot even if the call fails (never re-fire mid-task)
|
|
@@ -654,6 +662,7 @@ export class CortexOrchestrator {
|
|
|
654
662
|
`It is anchored to the REAL success criteria (what the task's own grader checks), NOT your own ` +
|
|
655
663
|
`tests. Your own tests are a means, never the finish line.\n\n${plan}\n</system-reminder>`,
|
|
656
664
|
});
|
|
665
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
657
666
|
if (store)
|
|
658
667
|
void store.recordEvent({
|
|
659
668
|
sessionId,
|
|
@@ -699,6 +708,7 @@ export class CortexOrchestrator {
|
|
|
699
708
|
return; // nothing authored (yet) — leave the one-shot open
|
|
700
709
|
this.reqLedgerDelivered = true;
|
|
701
710
|
lastMsg?.message?.content?.push?.({ type: 'text', text: `<system-reminder>\n${text}\n</system-reminder>` });
|
|
711
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
702
712
|
const store = this.getDecisionStore();
|
|
703
713
|
if (store)
|
|
704
714
|
void store.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'requirement_ledger_delivered', toolName: 'EndTurn', detail: { lines: (entries ?? []).length, chars: text.length } }).catch(() => { });
|
|
@@ -2102,6 +2112,7 @@ export class CortexOrchestrator {
|
|
|
2102
2112
|
}
|
|
2103
2113
|
};
|
|
2104
2114
|
messageHistoryForApi[messageHistoryForApi.length - 1] = userMessageForApi;
|
|
2115
|
+
this.rememberInjectedContent(userMessage.uuid, injectedContent); // R223 (CORTEX_PERSIST_INJECTED; off = no-op): later requests send the same bytes
|
|
2105
2116
|
// canonicalHistory will be re-computed after effectiveModel is determined
|
|
2106
2117
|
// (see input-slicing for cross-turn chain below).
|
|
2107
2118
|
// Debug logging for tools (already gathered above)
|
|
@@ -2169,7 +2180,7 @@ export class CortexOrchestrator {
|
|
|
2169
2180
|
const isPTCEnabled = this.config.enablePTC && effectiveModel.supportsPTC && effectiveModel.provider === 'anthropic';
|
|
2170
2181
|
// Deferred loading: filter tools for non-PTC paths (essential + recently-used only)
|
|
2171
2182
|
if (!isPTCEnabled && this.effectiveDeferredLoading && toolsToUse && toolsToUse.length > 0) {
|
|
2172
|
-
toolsToUse = this.toolFilter.getFilteredTools(toolsToUse);
|
|
2183
|
+
toolsToUse = this.toolFilter.getFilteredTools(toolsToUse, { appendOnly: resolveAppendOnlyTools() });
|
|
2173
2184
|
}
|
|
2174
2185
|
// First-turn anchoring narrows further while armed (no-op once lifted).
|
|
2175
2186
|
// Card-level home door: capture the active card's anchorProfile (env
|
|
@@ -2208,7 +2219,7 @@ export class CortexOrchestrator {
|
|
|
2208
2219
|
this.mentorshipMiddleware?.resetSequentialCalls(this.currentSessionId);
|
|
2209
2220
|
// §13-B2: force AskForAdvice this turn on high-confidence thrash (mentor-force armed).
|
|
2210
2221
|
const forcedMentorChoice = await this.resolveForcedMentorChoice(toolsToUse, 0); // initial request: 0 tool calls yet
|
|
2211
|
-
const
|
|
2222
|
+
const initialPrepOpts = {
|
|
2212
2223
|
temperature: options.parameters?.temperature,
|
|
2213
2224
|
maxTokens: options.parameters?.maxTokens,
|
|
2214
2225
|
topP: options.parameters?.topP,
|
|
@@ -2217,7 +2228,18 @@ export class CortexOrchestrator {
|
|
|
2217
2228
|
staticSystemPrompt: this.currentStaticSystemPrompt, // R28
|
|
2218
2229
|
conversationId: this.currentConversationId, // R28b
|
|
2219
2230
|
toolChoice: forcedMentorChoice // §13-B2 forced mentor tool_choice
|
|
2220
|
-
}
|
|
2231
|
+
};
|
|
2232
|
+
const preparedRequest = this.gatewayTranslation.prepareRequest(canonicalHistory, toolsToUse, effectiveModel, initialPrepOpts);
|
|
2233
|
+
// R228 fallback (dark): the unchained full-history twin of preparedRequest, built only if the held response is gone.
|
|
2234
|
+
const rebuildInitialFull = () => {
|
|
2235
|
+
const r = this.gatewayTranslation.prepareRequest(this.convertToCanonicalMessages(messageHistoryForApi), toolsToUse, effectiveModel, initialPrepOpts);
|
|
2236
|
+
r.conversationId = this.currentSessionId;
|
|
2237
|
+
if (isPTCEnabled && toolsToUse && toolsToUse.length > 0) {
|
|
2238
|
+
r.tools = this.gatewayTranslation.prepareToolsWithPTC(toolsToUse, effectiveModel);
|
|
2239
|
+
r.parameters.enablePTC = true;
|
|
2240
|
+
}
|
|
2241
|
+
return r;
|
|
2242
|
+
};
|
|
2221
2243
|
// Stateful Responses API: chain from prior response when available
|
|
2222
2244
|
// (works across user turns — lastResponseId is preserved between messages).
|
|
2223
2245
|
// Only affects 'responses' api.pattern; other providers ignore this field.
|
|
@@ -2254,8 +2276,11 @@ export class CortexOrchestrator {
|
|
|
2254
2276
|
// Use effectiveModel (which may have modified endpoint/pattern for server-side tools)
|
|
2255
2277
|
// Wrap API call with retry middleware for automatic retry on transient errors
|
|
2256
2278
|
if (this.retryMiddleware) {
|
|
2257
|
-
|
|
2258
|
-
apiResponse =
|
|
2279
|
+
let retryResult;
|
|
2280
|
+
apiResponse = await this.sendWithChainFallback(preparedRequest, async (r) => {
|
|
2281
|
+
retryResult = await this.retryMiddleware.executeWithRetry(() => this.apiClient.sendRequest(r, effectiveModel), 'primary_api_call');
|
|
2282
|
+
return retryResult.result;
|
|
2283
|
+
}, rebuildInitialFull, 'primary_api_call'); // R228 fallback (dark; off = one send)
|
|
2259
2284
|
// Log retry information if retries occurred
|
|
2260
2285
|
if (this.config.debug && retryResult.attemptCount > 1) {
|
|
2261
2286
|
console.log(`[Orchestrator] API call succeeded after ${retryResult.attemptCount} attempts`);
|
|
@@ -2264,7 +2289,7 @@ export class CortexOrchestrator {
|
|
|
2264
2289
|
}
|
|
2265
2290
|
else {
|
|
2266
2291
|
// Fallback: direct call if middleware not available
|
|
2267
|
-
apiResponse = await this.apiClient.sendRequest(
|
|
2292
|
+
apiResponse = await this.sendWithChainFallback(preparedRequest, (r) => this.apiClient.sendRequest(r, effectiveModel), rebuildInitialFull, 'primary_api_call'); // R228 fallback (dark)
|
|
2268
2293
|
}
|
|
2269
2294
|
}
|
|
2270
2295
|
catch (error) {
|
|
@@ -2696,6 +2721,7 @@ export class CortexOrchestrator {
|
|
|
2696
2721
|
if (isEmpty) {
|
|
2697
2722
|
m.message = m.message || { role: 'assistant' };
|
|
2698
2723
|
m.message.content = [{ type: 'text', text: '(no output)' }];
|
|
2724
|
+
this.invalidateConversions([m]); // R221 safety net
|
|
2699
2725
|
if (this.config.debug) {
|
|
2700
2726
|
console.log(`[Orchestrator] R26: repaired empty assistant message at history[${i}] before retry`);
|
|
2701
2727
|
}
|
|
@@ -2737,7 +2763,7 @@ export class CortexOrchestrator {
|
|
|
2737
2763
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
2738
2764
|
const retryEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param
|
|
2739
2765
|
const buildRetryRequest = () => {
|
|
2740
|
-
const retryCanonicalHistory = this.convertToCanonicalMessages(
|
|
2766
|
+
const retryCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
2741
2767
|
const req = this.gatewayTranslation.prepareRequest(retryCanonicalHistory, toolsToUse, effectiveModel, {
|
|
2742
2768
|
temperature: options.parameters?.temperature,
|
|
2743
2769
|
maxTokens: options.parameters?.maxTokens,
|
|
@@ -2786,6 +2812,7 @@ export class CortexOrchestrator {
|
|
|
2786
2812
|
...(retryConverted.usage && { usage: retryConverted.usage }),
|
|
2787
2813
|
};
|
|
2788
2814
|
this.messageHistory.push(retryAssistantMessage);
|
|
2815
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
2789
2816
|
await this.historyStore.appendMessage(this.currentSessionId, retryAssistantMessage);
|
|
2790
2817
|
// Update current pointer so the loop re-evaluates with new content.
|
|
2791
2818
|
currentAssistantMessage = retryAssistantMessage;
|
|
@@ -2857,28 +2884,28 @@ export class CortexOrchestrator {
|
|
|
2857
2884
|
this.messageHistory.push(inactionUserMessage);
|
|
2858
2885
|
await this.historyStore.appendMessage(this.currentSessionId, inactionUserMessage);
|
|
2859
2886
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
2860
|
-
const
|
|
2861
|
-
const
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2887
|
+
const inactionEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param (consumed once)
|
|
2888
|
+
const buildInactionRequest = () => {
|
|
2889
|
+
const inactionCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
2890
|
+
const inactionRequest = this.gatewayTranslation.prepareRequest(inactionCanonicalHistory, toolsToUse, effectiveModel, {
|
|
2891
|
+
temperature: options.parameters?.temperature,
|
|
2892
|
+
maxTokens: options.parameters?.maxTokens,
|
|
2893
|
+
topP: options.parameters?.topP,
|
|
2894
|
+
reasoningEffort: inactionEffort,
|
|
2895
|
+
stream: options.streaming,
|
|
2896
|
+
staticSystemPrompt: this.currentStaticSystemPrompt, // R28
|
|
2897
|
+
conversationId: this.currentConversationId, // R28b
|
|
2898
|
+
});
|
|
2899
|
+
if (this.lastResponseId && effectiveModel.api.pattern === 'responses') {
|
|
2900
|
+
inactionRequest.previousResponseId = this.lastResponseId;
|
|
2901
|
+
}
|
|
2902
|
+
inactionRequest.conversationId = this.currentSessionId;
|
|
2903
|
+
return inactionRequest;
|
|
2904
|
+
};
|
|
2905
|
+
const sendInactionRequest = async (r) => this.retryMiddleware
|
|
2906
|
+
? (await this.retryMiddleware.executeWithRetry(() => this.apiClient.sendRequest(r, effectiveModel), 'inaction_nudge_retry')).result
|
|
2907
|
+
: this.apiClient.sendRequest(r, effectiveModel);
|
|
2908
|
+
const inactionApiResponse = await this.sendWithChainFallback(buildInactionRequest(), sendInactionRequest, buildInactionRequest, 'inaction_nudge_retry'); // R228 fallback (dark; off = one send)
|
|
2882
2909
|
const inactionConverted = this.gatewayTranslation.convertResponse(inactionApiResponse.data, effectiveModel, {
|
|
2883
2910
|
sessionId: this.currentSessionId,
|
|
2884
2911
|
conversationId: this.currentConversationId,
|
|
@@ -2911,6 +2938,7 @@ export class CortexOrchestrator {
|
|
|
2911
2938
|
...(inactionConverted.usage && { usage: inactionConverted.usage }),
|
|
2912
2939
|
};
|
|
2913
2940
|
this.messageHistory.push(inactionAssistantMessage);
|
|
2941
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
2914
2942
|
await this.historyStore.appendMessage(this.currentSessionId, inactionAssistantMessage);
|
|
2915
2943
|
currentAssistantMessage = inactionAssistantMessage;
|
|
2916
2944
|
currentAssistantCanonicalMessage = inactionAssistantCanonical;
|
|
@@ -3026,6 +3054,7 @@ export class CortexOrchestrator {
|
|
|
3026
3054
|
if (isEmpty) {
|
|
3027
3055
|
m.message = m.message || { role: 'assistant' };
|
|
3028
3056
|
m.message.content = [{ type: 'text', text: '(no output)' }];
|
|
3057
|
+
this.invalidateConversions([m]); // R221 safety net
|
|
3029
3058
|
}
|
|
3030
3059
|
break;
|
|
3031
3060
|
}
|
|
@@ -3072,28 +3101,28 @@ export class CortexOrchestrator {
|
|
|
3072
3101
|
this.messageHistory.push(endTurnReminder);
|
|
3073
3102
|
await this.historyStore.appendMessage(this.currentSessionId, endTurnReminder);
|
|
3074
3103
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
3075
|
-
const
|
|
3076
|
-
const
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3104
|
+
const gateEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param (consumed once)
|
|
3105
|
+
const buildGateRequest = () => {
|
|
3106
|
+
const gateCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
3107
|
+
const gateRequest = this.gatewayTranslation.prepareRequest(gateCanonicalHistory, toolsToUse, effectiveModel, {
|
|
3108
|
+
temperature: options.parameters?.temperature,
|
|
3109
|
+
maxTokens: options.parameters?.maxTokens,
|
|
3110
|
+
topP: options.parameters?.topP,
|
|
3111
|
+
reasoningEffort: gateEffort,
|
|
3112
|
+
stream: options.streaming,
|
|
3113
|
+
staticSystemPrompt: this.currentStaticSystemPrompt,
|
|
3114
|
+
conversationId: this.currentConversationId,
|
|
3115
|
+
});
|
|
3116
|
+
if (this.lastResponseId && effectiveModel.api.pattern === 'responses') {
|
|
3117
|
+
gateRequest.previousResponseId = this.lastResponseId;
|
|
3118
|
+
}
|
|
3119
|
+
gateRequest.conversationId = this.currentSessionId;
|
|
3120
|
+
return gateRequest;
|
|
3121
|
+
};
|
|
3122
|
+
const sendGate = async (r) => this.retryMiddleware
|
|
3123
|
+
? (await this.retryMiddleware.executeWithRetry(() => this.apiClient.sendRequest(r, effectiveModel), 'end_turn_gate')).result
|
|
3124
|
+
: this.apiClient.sendRequest(r, effectiveModel);
|
|
3125
|
+
const gateApiResponse = await this.sendWithChainFallback(buildGateRequest(), sendGate, buildGateRequest, 'end_turn_gate'); // R228 fallback (dark; off = one send)
|
|
3097
3126
|
const gateConverted = this.gatewayTranslation.convertResponse(gateApiResponse.data, effectiveModel, {
|
|
3098
3127
|
sessionId: this.currentSessionId,
|
|
3099
3128
|
conversationId: this.currentConversationId,
|
|
@@ -3126,6 +3155,7 @@ export class CortexOrchestrator {
|
|
|
3126
3155
|
...(gateConverted.usage && { usage: gateConverted.usage }),
|
|
3127
3156
|
};
|
|
3128
3157
|
this.messageHistory.push(gateAssistantMessage);
|
|
3158
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
3129
3159
|
await this.historyStore.appendMessage(this.currentSessionId, gateAssistantMessage);
|
|
3130
3160
|
currentAssistantMessage = gateAssistantMessage;
|
|
3131
3161
|
currentAssistantCanonicalMessage = gateAssistantCanonical;
|
|
@@ -3586,6 +3616,7 @@ export class CortexOrchestrator {
|
|
|
3586
3616
|
const block = lastMsg.message.content[0];
|
|
3587
3617
|
const existing = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
3588
3618
|
block.content = existing + '\n\n' + signals;
|
|
3619
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
3589
3620
|
// Observability (backlog item 3, micro-suite defect #4): this
|
|
3590
3621
|
// mutation happens AFTER the tool_result was persisted — the
|
|
3591
3622
|
// durable session lacks the steering the model saw. Bank a
|
|
@@ -3720,7 +3751,7 @@ export class CortexOrchestrator {
|
|
|
3720
3751
|
// are included in the continuation request's tools array
|
|
3721
3752
|
if (this.effectiveDeferredLoading && !isPTCEnabled) {
|
|
3722
3753
|
const beforeCount = toolsToUse.length;
|
|
3723
|
-
toolsToUse = this.toolFilter.getFilteredTools(allTools);
|
|
3754
|
+
toolsToUse = this.toolFilter.getFilteredTools(allTools, { appendOnly: resolveAppendOnlyTools() });
|
|
3724
3755
|
// StructuredOutput: the re-filter rebuilds from allTools (which never
|
|
3725
3756
|
// contained the request-scoped tool) — re-append so it stays present
|
|
3726
3757
|
// on every request of the turn.
|
|
@@ -4025,7 +4056,7 @@ export class CortexOrchestrator {
|
|
|
4025
4056
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
4026
4057
|
const synthEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param
|
|
4027
4058
|
const buildSynthRequest = () => {
|
|
4028
|
-
const synthCanonicalHistory = this.convertToCanonicalMessages(
|
|
4059
|
+
const synthCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
4029
4060
|
const req = this.gatewayTranslation.prepareRequest(synthCanonicalHistory,
|
|
4030
4061
|
// Tools suppressed — the model MUST produce text, not call more tools.
|
|
4031
4062
|
// NOTE (structured-output constraint, 2026-08-02): this is R29a's
|
|
@@ -4082,6 +4113,7 @@ export class CortexOrchestrator {
|
|
|
4082
4113
|
...(synthConverted.usage && { usage: synthConverted.usage }),
|
|
4083
4114
|
};
|
|
4084
4115
|
this.messageHistory.push(synthAssistantMessage);
|
|
4116
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
4085
4117
|
await this.historyStore.appendMessage(this.currentSessionId, synthAssistantMessage);
|
|
4086
4118
|
this.sessionTimeline.recordMessage(synthAssistantMessage.uuid, 'assistant');
|
|
4087
4119
|
if (synthConverted.usage) {
|
|
@@ -4484,6 +4516,7 @@ export class CortexOrchestrator {
|
|
|
4484
4516
|
}
|
|
4485
4517
|
};
|
|
4486
4518
|
messageHistoryForApi[messageHistoryForApi.length - 1] = userMessageForApi;
|
|
4519
|
+
this.rememberInjectedContent(userMessage.uuid, injectedContent); // R223 (CORTEX_PERSIST_INJECTED; off = no-op): later requests send the same bytes
|
|
4487
4520
|
// canonicalHistory computed after effectiveModel is determined (input-slicing below).
|
|
4488
4521
|
// Tools already gathered above for middleware injection
|
|
4489
4522
|
// Server-side tool detection
|
|
@@ -4511,7 +4544,7 @@ export class CortexOrchestrator {
|
|
|
4511
4544
|
const isPTCEnabled = this.config.enablePTC && effectiveModel.supportsPTC && effectiveModel.provider === 'anthropic';
|
|
4512
4545
|
// Deferred loading: filter tools for non-PTC paths (essential + recently-used only)
|
|
4513
4546
|
if (!isPTCEnabled && this.effectiveDeferredLoading && toolsToUse && toolsToUse.length > 0) {
|
|
4514
|
-
toolsToUse = this.toolFilter.getFilteredTools(toolsToUse);
|
|
4547
|
+
toolsToUse = this.toolFilter.getFilteredTools(toolsToUse, { appendOnly: resolveAppendOnlyTools() });
|
|
4515
4548
|
}
|
|
4516
4549
|
// First-turn anchoring narrows further while armed (no-op once lifted).
|
|
4517
4550
|
// Card-level home door: capture the active card's anchorProfile (env
|
|
@@ -4906,6 +4939,7 @@ export class CortexOrchestrator {
|
|
|
4906
4939
|
if (isEmpty) {
|
|
4907
4940
|
m.message = m.message || { role: 'assistant' };
|
|
4908
4941
|
m.message.content = [{ type: 'text', text: '(no output)' }];
|
|
4942
|
+
this.invalidateConversions([m]); // R221 safety net
|
|
4909
4943
|
}
|
|
4910
4944
|
break;
|
|
4911
4945
|
}
|
|
@@ -4939,7 +4973,7 @@ export class CortexOrchestrator {
|
|
|
4939
4973
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
4940
4974
|
const retryEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param
|
|
4941
4975
|
const buildRetryRequest = () => {
|
|
4942
|
-
const retryCanonicalHistory = this.convertToCanonicalMessages(
|
|
4976
|
+
const retryCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
4943
4977
|
const req = this.gatewayTranslation.prepareRequest(retryCanonicalHistory, toolsToUse, effectiveModel, {
|
|
4944
4978
|
temperature: options.parameters?.temperature,
|
|
4945
4979
|
maxTokens: options.parameters?.maxTokens,
|
|
@@ -4988,6 +5022,7 @@ export class CortexOrchestrator {
|
|
|
4988
5022
|
...(retryConverted.usage && { usage: retryConverted.usage }),
|
|
4989
5023
|
};
|
|
4990
5024
|
this.messageHistory.push(retryAssistantMessage);
|
|
5025
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
4991
5026
|
await this.historyStore.appendMessage(this.currentSessionId, retryAssistantMessage);
|
|
4992
5027
|
currentAssistantCanonicalMessage = retryAssistantCanonical;
|
|
4993
5028
|
lastStopReason = retryConverted.stopReason; // R153
|
|
@@ -5065,28 +5100,28 @@ export class CortexOrchestrator {
|
|
|
5065
5100
|
await this.historyStore.appendMessage(this.currentSessionId, inactionUserMessage);
|
|
5066
5101
|
try {
|
|
5067
5102
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
5068
|
-
const
|
|
5069
|
-
const
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5103
|
+
const retryEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param (consumed once)
|
|
5104
|
+
const buildRetryRequest = () => {
|
|
5105
|
+
const retryCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
5106
|
+
const retryRequest = this.gatewayTranslation.prepareRequest(retryCanonicalHistory, toolsToUse, effectiveModel, {
|
|
5107
|
+
temperature: options.parameters?.temperature,
|
|
5108
|
+
maxTokens: options.parameters?.maxTokens,
|
|
5109
|
+
topP: options.parameters?.topP,
|
|
5110
|
+
reasoningEffort: retryEffort,
|
|
5111
|
+
stream: false,
|
|
5112
|
+
staticSystemPrompt: this.currentStaticSystemPrompt,
|
|
5113
|
+
conversationId: this.currentConversationId,
|
|
5114
|
+
});
|
|
5115
|
+
if (this.lastResponseId && effectiveModel.api.pattern === 'responses') {
|
|
5116
|
+
retryRequest.previousResponseId = this.lastResponseId;
|
|
5117
|
+
}
|
|
5118
|
+
retryRequest.conversationId = this.currentSessionId;
|
|
5119
|
+
return retryRequest;
|
|
5120
|
+
};
|
|
5121
|
+
const sendRetryRequest = async (r) => this.retryMiddleware
|
|
5122
|
+
? (await this.retryMiddleware.executeWithRetry(() => this.apiClient.sendRequest(r, effectiveModel), 'inaction_nudge_retry')).result
|
|
5123
|
+
: this.apiClient.sendRequest(r, effectiveModel);
|
|
5124
|
+
const retryApiResponse = await this.sendWithChainFallback(buildRetryRequest(), sendRetryRequest, buildRetryRequest, 'inaction_nudge_retry'); // R228 fallback (dark; off = one send)
|
|
5090
5125
|
const retryConverted = this.gatewayTranslation.convertResponse(retryApiResponse.data, effectiveModel, {
|
|
5091
5126
|
sessionId: this.currentSessionId,
|
|
5092
5127
|
conversationId: this.currentConversationId,
|
|
@@ -5119,6 +5154,7 @@ export class CortexOrchestrator {
|
|
|
5119
5154
|
...(retryConverted.usage && { usage: retryConverted.usage }),
|
|
5120
5155
|
};
|
|
5121
5156
|
this.messageHistory.push(retryAssistantMessage);
|
|
5157
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
5122
5158
|
await this.historyStore.appendMessage(this.currentSessionId, retryAssistantMessage);
|
|
5123
5159
|
currentAssistantCanonicalMessage = retryAssistantCanonical;
|
|
5124
5160
|
lastStopReason = retryConverted.stopReason; // R153
|
|
@@ -5425,6 +5461,7 @@ export class CortexOrchestrator {
|
|
|
5425
5461
|
const block = lastMsg.message.content[0];
|
|
5426
5462
|
const existing = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
5427
5463
|
block.content = existing + '\n\n' + signals;
|
|
5464
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
5428
5465
|
// Observability (backlog item 3): post-persist mutation — bank a
|
|
5429
5466
|
// steering_injected event (streaming parity).
|
|
5430
5467
|
const store = this.getDecisionStore();
|
|
@@ -5541,7 +5578,7 @@ export class CortexOrchestrator {
|
|
|
5541
5578
|
}
|
|
5542
5579
|
// Re-filter tools after SearchTools discovery (same as sendMessage path)
|
|
5543
5580
|
if (this.effectiveDeferredLoading && !isPTCEnabled) {
|
|
5544
|
-
toolsToUse = this.toolFilter.getFilteredTools(allTools);
|
|
5581
|
+
toolsToUse = this.toolFilter.getFilteredTools(allTools, { appendOnly: resolveAppendOnlyTools() });
|
|
5545
5582
|
// StructuredOutput: re-filter rebuilds from allTools — re-append the
|
|
5546
5583
|
// request-scoped tool (same as the sendMessage continuation path).
|
|
5547
5584
|
if (structuredOutputState) {
|
|
@@ -5932,7 +5969,7 @@ export class CortexOrchestrator {
|
|
|
5932
5969
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
5933
5970
|
const synthEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param
|
|
5934
5971
|
const buildSynthRequest = () => {
|
|
5935
|
-
const synthCanonicalHistory = this.convertToCanonicalMessages(
|
|
5972
|
+
const synthCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
5936
5973
|
const req = this.gatewayTranslation.prepareRequest(synthCanonicalHistory, [], // tools suppressed — must produce text
|
|
5937
5974
|
effectiveModel, {
|
|
5938
5975
|
temperature: options.parameters?.temperature,
|
|
@@ -5986,6 +6023,7 @@ export class CortexOrchestrator {
|
|
|
5986
6023
|
...(synthConverted.usage && { usage: synthConverted.usage }),
|
|
5987
6024
|
};
|
|
5988
6025
|
this.messageHistory.push(synthAssistantMessage);
|
|
6026
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
5989
6027
|
await this.historyStore.appendMessage(this.currentSessionId, synthAssistantMessage);
|
|
5990
6028
|
this.sessionTimeline.recordMessage(synthAssistantMessage.uuid, 'assistant');
|
|
5991
6029
|
currentAssistantCanonicalMessage = synthCanonical;
|
|
@@ -6335,6 +6373,7 @@ export class CortexOrchestrator {
|
|
|
6335
6373
|
// cleared on session reset so stale entries (including synthetic-repair
|
|
6336
6374
|
// uuids from the previous session) can't leak into the new session.
|
|
6337
6375
|
this.canonicalConversionCache.clear();
|
|
6376
|
+
this.injectedApiContent.clear(); // R223
|
|
6338
6377
|
// Reset XAI Responses API chain for new session (was leaking across `--new`).
|
|
6339
6378
|
// Without this, a new session would reuse lastResponseId from the previous
|
|
6340
6379
|
// session, sending previous_response_id that the server doesn't recognize
|
|
@@ -6464,6 +6503,7 @@ export class CortexOrchestrator {
|
|
|
6464
6503
|
// Round 6: clear canonical-conversion cache on session resume too —
|
|
6465
6504
|
// entries from a previously running orchestrator instance are stale.
|
|
6466
6505
|
this.canonicalConversionCache.clear();
|
|
6506
|
+
this.injectedApiContent.clear(); // R223
|
|
6467
6507
|
// Reuse the persistedMetadata loaded above — avoid a second disk read.
|
|
6468
6508
|
const savedMetadata = persistedMetadata;
|
|
6469
6509
|
if (savedMetadata?.cacheMetrics) {
|
|
@@ -6934,6 +6974,7 @@ export class CortexOrchestrator {
|
|
|
6934
6974
|
plateau: plateauStatusSegment(this.plateauTracker), // R210: null when CORTEX_PLATEAU_STOP is off or no metric seen → line unchanged
|
|
6935
6975
|
});
|
|
6936
6976
|
if (appendStatusToNewestToolResult(this.messageHistory, line)) {
|
|
6977
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
6937
6978
|
this.turnStatusLines++;
|
|
6938
6979
|
if (this.config.debug)
|
|
6939
6980
|
console.log(`[TurnStatus] iteration ${iteration}: ${line}`);
|
|
@@ -9385,7 +9426,17 @@ export class CortexOrchestrator {
|
|
|
9385
9426
|
try {
|
|
9386
9427
|
if (!isReasoningExhaustion(cls) || !resolveWallDrop())
|
|
9387
9428
|
return { dropped: false };
|
|
9429
|
+
// R228 (CORTEX_RESPONSES_SLICE_ALL): on a chained Responses session the server already HOLDS the walled turn (previous_response_id
|
|
9430
|
+
// = its id) — a local drop cannot remove it, and the nudge on an already-held tool_result would never be sent (sliced away). Keep
|
|
9431
|
+
// the turn locally so the caller pushes the nudge as a new input item.
|
|
9432
|
+
const tail = this.messageHistory[this.messageHistory.length - 1];
|
|
9433
|
+
if (resolveResponsesSliceAll() && this.lastResponseId && tail?.model?.apiPattern === 'responses')
|
|
9434
|
+
return { dropped: false };
|
|
9388
9435
|
const r = dropWalledTurn(this.messageHistory, nudge);
|
|
9436
|
+
// R221 (CORTEX_WALL_CACHE_FIX): the carrier tool_result was converted (and cached by uuid) while the walled turn was the tail —
|
|
9437
|
+
// drop that stale copy so every later request carries the nudge (+ wall summary), not just the retry.
|
|
9438
|
+
if (r.dropped && r.carrierMessage)
|
|
9439
|
+
this.invalidateConversions([r.carrierMessage]);
|
|
9389
9440
|
const store = this.getDecisionStore();
|
|
9390
9441
|
if (r.dropped && store)
|
|
9391
9442
|
void store.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'wall_drop', detail: { iteration, droppedChars: r.droppedChars, carrier: r.carrier, streaming } }).catch(() => { });
|
|
@@ -9410,6 +9461,7 @@ export class CortexOrchestrator {
|
|
|
9410
9461
|
return;
|
|
9411
9462
|
if (!appendStatusToNewestToolResult(this.messageHistory, buildComputeNudgeLine(reasoningTokens)))
|
|
9412
9463
|
return;
|
|
9464
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
9413
9465
|
this.computeNudgeLastRound = round;
|
|
9414
9466
|
this.computeNudgeFired++;
|
|
9415
9467
|
const store = this.getDecisionStore();
|
|
@@ -9451,7 +9503,7 @@ export class CortexOrchestrator {
|
|
|
9451
9503
|
? (await this.retryMiddleware.executeWithRetry(() => this.apiClient.sendRequest(req, model), label)).result
|
|
9452
9504
|
: this.apiClient.sendRequest(req, model);
|
|
9453
9505
|
try {
|
|
9454
|
-
return await send(build())
|
|
9506
|
+
return await this.sendWithChainFallback(build(), send, build, label); // R228 fallback (dark; off = send(build()))
|
|
9455
9507
|
}
|
|
9456
9508
|
catch (err) {
|
|
9457
9509
|
if (!isImageRejectionError(err))
|
|
@@ -9460,6 +9512,7 @@ export class CortexOrchestrator {
|
|
|
9460
9512
|
const stripped = stripRejectedImages(this.messageHistory, reason);
|
|
9461
9513
|
if (stripped === 0)
|
|
9462
9514
|
throw err;
|
|
9515
|
+
this.invalidateConversions(this.messageHistory); // R221 safety net: stubbed image blocks live in already-converted messages
|
|
9463
9516
|
console.warn(`[Orchestrator] R154: provider rejected an image block on ${label} — stubbed ${stripped} image block(s) in history and retrying once.`);
|
|
9464
9517
|
const store = this.getDecisionStore();
|
|
9465
9518
|
if (store)
|
|
@@ -9967,6 +10020,11 @@ export class CortexOrchestrator {
|
|
|
9967
10020
|
* so this cleanup just removes them from the in-memory array.
|
|
9968
10021
|
*/
|
|
9969
10022
|
cleanupEphemeralMessages() {
|
|
10023
|
+
// R227 (CORTEX_KEEP_MENTOR_MESSAGES=on, dark): keep them — removing a mid-history message rewrites every later request's prefix and
|
|
10024
|
+
// shifts the Responses slice checkpoint (messageCountAtLastResponse counts them). Kept, the history stays append-only and the
|
|
10025
|
+
// checkpoint stays exact. Display already strips their system-reminder (sanitizeHistoryForDisplay); they were never persisted.
|
|
10026
|
+
if (resolveKeepMentorMessages())
|
|
10027
|
+
return;
|
|
9970
10028
|
const beforeCount = this.messageHistory.length;
|
|
9971
10029
|
this.messageHistory = this.messageHistory.filter((msg) => !msg.metadata?.ephemeral);
|
|
9972
10030
|
const removedCount = beforeCount - this.messageHistory.length;
|
|
@@ -10359,7 +10417,78 @@ export class CortexOrchestrator {
|
|
|
10359
10417
|
* Also validates and repairs the message history to handle crash recovery scenarios
|
|
10360
10418
|
* where tool_use blocks might be orphaned without matching tool_results.
|
|
10361
10419
|
*/
|
|
10362
|
-
|
|
10420
|
+
/** R223 (CORTEX_PERSIST_INJECTED, dark): uuid → the injected user content the first request of that turn sent. The message in
|
|
10421
|
+
* messageHistory keeps the RAW content (storage, display, title, judges and every text reader see the raw text); only the API
|
|
10422
|
+
* conversion swaps the injected content in, so every request of the session carries the same bytes for that message. */
|
|
10423
|
+
injectedApiContent = new Map();
|
|
10424
|
+
rememberInjectedContent(uuid, injected) {
|
|
10425
|
+
if (resolvePersistInjected())
|
|
10426
|
+
this.injectedApiContent.set(uuid, injected);
|
|
10427
|
+
}
|
|
10428
|
+
/** The record convertSingleMessage sees for the API (R223 swap; identity when off / not injected). */
|
|
10429
|
+
apiViewOf(msg) {
|
|
10430
|
+
if (this.injectedApiContent.size === 0 || !msg?.uuid)
|
|
10431
|
+
return msg;
|
|
10432
|
+
const injected = this.injectedApiContent.get(msg.uuid);
|
|
10433
|
+
if (injected === undefined)
|
|
10434
|
+
return msg;
|
|
10435
|
+
return { ...msg, message: { ...msg.message, content: injected } };
|
|
10436
|
+
}
|
|
10437
|
+
/** R221 general safety net (CORTEX_WALL_CACHE_FIX=on, dark; off = no-op): drop the cached canonical conversion of
|
|
10438
|
+
* messages mutated in place, so the next request converts them fresh instead of re-sending a stale copy. */
|
|
10439
|
+
invalidateConversions(msgs) {
|
|
10440
|
+
if (!resolveWallCacheFix())
|
|
10441
|
+
return;
|
|
10442
|
+
for (const m of msgs)
|
|
10443
|
+
if (m?.uuid)
|
|
10444
|
+
this.canonicalConversionCache.delete(m.uuid);
|
|
10445
|
+
}
|
|
10446
|
+
/** R221 safety net for the steering carriers (all of them write into the messages after the newest assistant turn). */
|
|
10447
|
+
invalidateTailUnitConversions() {
|
|
10448
|
+
if (!resolveWallCacheFix())
|
|
10449
|
+
return;
|
|
10450
|
+
this.invalidateConversions(tailUnitAfterLastAssistant(this.messageHistory));
|
|
10451
|
+
}
|
|
10452
|
+
/** R228 (CORTEX_RESPONSES_SLICE_ALL, dark): history for a request that chains previous_response_id — only the items added since the
|
|
10453
|
+
* held response. Off / not Responses / no chain / no checkpoint → the full history (the pre-existing behaviour). */
|
|
10454
|
+
chainedHistoryForApi(model) {
|
|
10455
|
+
const h = [...this.messageHistory];
|
|
10456
|
+
if (!resolveResponsesSliceAll() || !this.lastResponseId || model.api.pattern !== 'responses')
|
|
10457
|
+
return h;
|
|
10458
|
+
const from = chainedSliceStart(this.messageCountAtLastResponse, h.length);
|
|
10459
|
+
return from > 0 ? h.slice(from) : h;
|
|
10460
|
+
}
|
|
10461
|
+
/** R228 (CORTEX_RESPONSES_SLICE_ALL) fallback: when the held response is gone (store off / expired / error naming it), drop the chain
|
|
10462
|
+
* and resend ONCE with the FULL history and NO previous_response_id — never both. `rebuildFull` is called AFTER the chain reset, so
|
|
10463
|
+
* every builder that reads this.lastResponseId / chainedHistoryForApi produces the unchained full-history request. Off, unchained, or
|
|
10464
|
+
* any other error → rethrown untouched (byte-identical). */
|
|
10465
|
+
async sendWithChainFallback(req, send, rebuildFull, label) {
|
|
10466
|
+
try {
|
|
10467
|
+
return await send(req);
|
|
10468
|
+
}
|
|
10469
|
+
catch (err) {
|
|
10470
|
+
if (!req.previousResponseId || !resolveResponsesSliceAll() || !isPreviousResponseUnavailable(err))
|
|
10471
|
+
throw err;
|
|
10472
|
+
const lost = req.previousResponseId;
|
|
10473
|
+
this.lastResponseId = null;
|
|
10474
|
+
this.lastResponseIdProvider = null;
|
|
10475
|
+
this.messageCountAtLastResponse = 0;
|
|
10476
|
+
const full = rebuildFull();
|
|
10477
|
+
delete full.previousResponseId; // never both: full history XOR a chain id
|
|
10478
|
+
console.warn(`[Orchestrator] R228: previous response ${lost} unavailable on ${label} — chain dropped, resending the full history once.`);
|
|
10479
|
+
const store = this.getDecisionStore();
|
|
10480
|
+
if (store)
|
|
10481
|
+
void store.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'responses_chain_fallback', detail: { label, reason: String(err?.message ?? err).slice(0, 160) } }).catch(() => { });
|
|
10482
|
+
return await send(full);
|
|
10483
|
+
}
|
|
10484
|
+
}
|
|
10485
|
+
/** R228: a chained non-continuation response (gate / retries / synthesis) was pushed — the server now holds everything up to here. */
|
|
10486
|
+
markChainedResponseCheckpoint(model) {
|
|
10487
|
+
if (resolveResponsesSliceAll() && model.api.pattern === 'responses')
|
|
10488
|
+
this.messageCountAtLastResponse = this.messageHistory.length;
|
|
10489
|
+
}
|
|
10490
|
+
convertToCanonicalMessages(rawMessages) {
|
|
10491
|
+
const messages = this.injectedApiContent.size > 0 ? rawMessages.map((m) => this.apiViewOf(m)) : rawMessages;
|
|
10363
10492
|
const tailIdx = messages.length - 1;
|
|
10364
10493
|
const converted = messages.map((msg, i) => {
|
|
10365
10494
|
// Tail is recomputed every call (see cache doc-comment above).
|