@nexus-cortex/core 4.124.44 → 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 +103 -0
- package/dist/adapters/GatewayTranslationLayer.d.ts.map +1 -1
- package/dist/adapters/GatewayTranslationLayer.js +4 -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/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +12 -0
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/orchestrator/APIClient.d.ts.map +1 -1
- package/dist/orchestrator/APIClient.js +66 -10
- 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 +207 -81
- package/dist/orchestrator/CortexOrchestrator.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/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/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
|
@@ -91,6 +91,7 @@ import { InitCortexContext, MemoryWrite, MemoryRecall } from '../tools/context-m
|
|
|
91
91
|
import { pruneAgedToolResults } from '../conversation/ToolResultPruner.js';
|
|
92
92
|
import { detectTailRepetition, tailLoopGuardEnabled } from './tailRepetitionDetector.js';
|
|
93
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)
|
|
94
95
|
import { resolveWallDrop, resolveOutputCap, dropWalledTurn, resolveWallSummary, extractWalledReasoning, clipReasoning, formatWallSummary } from './wallGuard.js';
|
|
95
96
|
import { resolvePid1Guard, detectPid1Note } from './pid1Guard.js'; // R209
|
|
96
97
|
import { PlateauStopTracker, plateauBootInstruction, plateauStatusSegment } from './plateauStop.js'; // R210
|
|
@@ -382,6 +383,7 @@ export class CortexOrchestrator {
|
|
|
382
383
|
type: 'text',
|
|
383
384
|
text: `<system-reminder>\nFull session context follows (deferred until your first action; applies from here on).\n</system-reminder>\n${corpus}`
|
|
384
385
|
});
|
|
386
|
+
this.invalidateTailUnitConversions(); // R221 safety net (CORTEX_WALL_CACHE_FIX; off = no-op)
|
|
385
387
|
if (this.config.debug) {
|
|
386
388
|
console.log(`[Anchor] deferred static corpus delivered at lift (${corpus.length} chars)`);
|
|
387
389
|
}
|
|
@@ -424,6 +426,7 @@ export class CortexOrchestrator {
|
|
|
424
426
|
if (!r)
|
|
425
427
|
return;
|
|
426
428
|
lastMsg.message.content.push({ type: 'text', text: `<system-reminder>\n${r.note}\n</system-reminder>` });
|
|
429
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
427
430
|
const store = this.getDecisionStore();
|
|
428
431
|
if (store)
|
|
429
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(() => { });
|
|
@@ -447,6 +450,7 @@ export class CortexOrchestrator {
|
|
|
447
450
|
return; // not the boundary yet — leave the one-shot open
|
|
448
451
|
this.plateauInstructionDelivered = true;
|
|
449
452
|
lastMsg.message.content.push({ type: 'text', text: `<system-reminder>\n${text}\n</system-reminder>` });
|
|
453
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
450
454
|
if (this.config.debug)
|
|
451
455
|
console.log('[PlateauStop] boot instruction delivered');
|
|
452
456
|
}
|
|
@@ -521,6 +525,7 @@ export class CortexOrchestrator {
|
|
|
521
525
|
text: `<system-reminder>\n${parts.join(' ')}\n</system-reminder>`
|
|
522
526
|
});
|
|
523
527
|
this.liftNudgeDelivered = true; // one-shot: consumed only on ACTUAL delivery
|
|
528
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
524
529
|
if (this.config.debug) {
|
|
525
530
|
console.log(`[Anchor] lift nudge delivered (${parts.join(' ').length} chars, ${hidden.length} hidden tools)`);
|
|
526
531
|
}
|
|
@@ -657,6 +662,7 @@ export class CortexOrchestrator {
|
|
|
657
662
|
`It is anchored to the REAL success criteria (what the task's own grader checks), NOT your own ` +
|
|
658
663
|
`tests. Your own tests are a means, never the finish line.\n\n${plan}\n</system-reminder>`,
|
|
659
664
|
});
|
|
665
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
660
666
|
if (store)
|
|
661
667
|
void store.recordEvent({
|
|
662
668
|
sessionId,
|
|
@@ -702,6 +708,7 @@ export class CortexOrchestrator {
|
|
|
702
708
|
return; // nothing authored (yet) — leave the one-shot open
|
|
703
709
|
this.reqLedgerDelivered = true;
|
|
704
710
|
lastMsg?.message?.content?.push?.({ type: 'text', text: `<system-reminder>\n${text}\n</system-reminder>` });
|
|
711
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
705
712
|
const store = this.getDecisionStore();
|
|
706
713
|
if (store)
|
|
707
714
|
void store.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'requirement_ledger_delivered', toolName: 'EndTurn', detail: { lines: (entries ?? []).length, chars: text.length } }).catch(() => { });
|
|
@@ -2105,6 +2112,7 @@ export class CortexOrchestrator {
|
|
|
2105
2112
|
}
|
|
2106
2113
|
};
|
|
2107
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
|
|
2108
2116
|
// canonicalHistory will be re-computed after effectiveModel is determined
|
|
2109
2117
|
// (see input-slicing for cross-turn chain below).
|
|
2110
2118
|
// Debug logging for tools (already gathered above)
|
|
@@ -2172,7 +2180,7 @@ export class CortexOrchestrator {
|
|
|
2172
2180
|
const isPTCEnabled = this.config.enablePTC && effectiveModel.supportsPTC && effectiveModel.provider === 'anthropic';
|
|
2173
2181
|
// Deferred loading: filter tools for non-PTC paths (essential + recently-used only)
|
|
2174
2182
|
if (!isPTCEnabled && this.effectiveDeferredLoading && toolsToUse && toolsToUse.length > 0) {
|
|
2175
|
-
toolsToUse = this.toolFilter.getFilteredTools(toolsToUse);
|
|
2183
|
+
toolsToUse = this.toolFilter.getFilteredTools(toolsToUse, { appendOnly: resolveAppendOnlyTools() });
|
|
2176
2184
|
}
|
|
2177
2185
|
// First-turn anchoring narrows further while armed (no-op once lifted).
|
|
2178
2186
|
// Card-level home door: capture the active card's anchorProfile (env
|
|
@@ -2211,7 +2219,7 @@ export class CortexOrchestrator {
|
|
|
2211
2219
|
this.mentorshipMiddleware?.resetSequentialCalls(this.currentSessionId);
|
|
2212
2220
|
// §13-B2: force AskForAdvice this turn on high-confidence thrash (mentor-force armed).
|
|
2213
2221
|
const forcedMentorChoice = await this.resolveForcedMentorChoice(toolsToUse, 0); // initial request: 0 tool calls yet
|
|
2214
|
-
const
|
|
2222
|
+
const initialPrepOpts = {
|
|
2215
2223
|
temperature: options.parameters?.temperature,
|
|
2216
2224
|
maxTokens: options.parameters?.maxTokens,
|
|
2217
2225
|
topP: options.parameters?.topP,
|
|
@@ -2220,7 +2228,18 @@ export class CortexOrchestrator {
|
|
|
2220
2228
|
staticSystemPrompt: this.currentStaticSystemPrompt, // R28
|
|
2221
2229
|
conversationId: this.currentConversationId, // R28b
|
|
2222
2230
|
toolChoice: forcedMentorChoice // §13-B2 forced mentor tool_choice
|
|
2223
|
-
}
|
|
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
|
+
};
|
|
2224
2243
|
// Stateful Responses API: chain from prior response when available
|
|
2225
2244
|
// (works across user turns — lastResponseId is preserved between messages).
|
|
2226
2245
|
// Only affects 'responses' api.pattern; other providers ignore this field.
|
|
@@ -2257,8 +2276,11 @@ export class CortexOrchestrator {
|
|
|
2257
2276
|
// Use effectiveModel (which may have modified endpoint/pattern for server-side tools)
|
|
2258
2277
|
// Wrap API call with retry middleware for automatic retry on transient errors
|
|
2259
2278
|
if (this.retryMiddleware) {
|
|
2260
|
-
|
|
2261
|
-
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)
|
|
2262
2284
|
// Log retry information if retries occurred
|
|
2263
2285
|
if (this.config.debug && retryResult.attemptCount > 1) {
|
|
2264
2286
|
console.log(`[Orchestrator] API call succeeded after ${retryResult.attemptCount} attempts`);
|
|
@@ -2267,7 +2289,7 @@ export class CortexOrchestrator {
|
|
|
2267
2289
|
}
|
|
2268
2290
|
else {
|
|
2269
2291
|
// Fallback: direct call if middleware not available
|
|
2270
|
-
apiResponse = await this.apiClient.sendRequest(
|
|
2292
|
+
apiResponse = await this.sendWithChainFallback(preparedRequest, (r) => this.apiClient.sendRequest(r, effectiveModel), rebuildInitialFull, 'primary_api_call'); // R228 fallback (dark)
|
|
2271
2293
|
}
|
|
2272
2294
|
}
|
|
2273
2295
|
catch (error) {
|
|
@@ -2699,6 +2721,7 @@ export class CortexOrchestrator {
|
|
|
2699
2721
|
if (isEmpty) {
|
|
2700
2722
|
m.message = m.message || { role: 'assistant' };
|
|
2701
2723
|
m.message.content = [{ type: 'text', text: '(no output)' }];
|
|
2724
|
+
this.invalidateConversions([m]); // R221 safety net
|
|
2702
2725
|
if (this.config.debug) {
|
|
2703
2726
|
console.log(`[Orchestrator] R26: repaired empty assistant message at history[${i}] before retry`);
|
|
2704
2727
|
}
|
|
@@ -2740,7 +2763,7 @@ export class CortexOrchestrator {
|
|
|
2740
2763
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
2741
2764
|
const retryEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param
|
|
2742
2765
|
const buildRetryRequest = () => {
|
|
2743
|
-
const retryCanonicalHistory = this.convertToCanonicalMessages(
|
|
2766
|
+
const retryCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
2744
2767
|
const req = this.gatewayTranslation.prepareRequest(retryCanonicalHistory, toolsToUse, effectiveModel, {
|
|
2745
2768
|
temperature: options.parameters?.temperature,
|
|
2746
2769
|
maxTokens: options.parameters?.maxTokens,
|
|
@@ -2789,6 +2812,7 @@ export class CortexOrchestrator {
|
|
|
2789
2812
|
...(retryConverted.usage && { usage: retryConverted.usage }),
|
|
2790
2813
|
};
|
|
2791
2814
|
this.messageHistory.push(retryAssistantMessage);
|
|
2815
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
2792
2816
|
await this.historyStore.appendMessage(this.currentSessionId, retryAssistantMessage);
|
|
2793
2817
|
// Update current pointer so the loop re-evaluates with new content.
|
|
2794
2818
|
currentAssistantMessage = retryAssistantMessage;
|
|
@@ -2860,28 +2884,28 @@ export class CortexOrchestrator {
|
|
|
2860
2884
|
this.messageHistory.push(inactionUserMessage);
|
|
2861
2885
|
await this.historyStore.appendMessage(this.currentSessionId, inactionUserMessage);
|
|
2862
2886
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
2863
|
-
const
|
|
2864
|
-
const
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
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)
|
|
2885
2909
|
const inactionConverted = this.gatewayTranslation.convertResponse(inactionApiResponse.data, effectiveModel, {
|
|
2886
2910
|
sessionId: this.currentSessionId,
|
|
2887
2911
|
conversationId: this.currentConversationId,
|
|
@@ -2914,6 +2938,7 @@ export class CortexOrchestrator {
|
|
|
2914
2938
|
...(inactionConverted.usage && { usage: inactionConverted.usage }),
|
|
2915
2939
|
};
|
|
2916
2940
|
this.messageHistory.push(inactionAssistantMessage);
|
|
2941
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
2917
2942
|
await this.historyStore.appendMessage(this.currentSessionId, inactionAssistantMessage);
|
|
2918
2943
|
currentAssistantMessage = inactionAssistantMessage;
|
|
2919
2944
|
currentAssistantCanonicalMessage = inactionAssistantCanonical;
|
|
@@ -3029,6 +3054,7 @@ export class CortexOrchestrator {
|
|
|
3029
3054
|
if (isEmpty) {
|
|
3030
3055
|
m.message = m.message || { role: 'assistant' };
|
|
3031
3056
|
m.message.content = [{ type: 'text', text: '(no output)' }];
|
|
3057
|
+
this.invalidateConversions([m]); // R221 safety net
|
|
3032
3058
|
}
|
|
3033
3059
|
break;
|
|
3034
3060
|
}
|
|
@@ -3075,28 +3101,28 @@ export class CortexOrchestrator {
|
|
|
3075
3101
|
this.messageHistory.push(endTurnReminder);
|
|
3076
3102
|
await this.historyStore.appendMessage(this.currentSessionId, endTurnReminder);
|
|
3077
3103
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
3078
|
-
const
|
|
3079
|
-
const
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
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)
|
|
3100
3126
|
const gateConverted = this.gatewayTranslation.convertResponse(gateApiResponse.data, effectiveModel, {
|
|
3101
3127
|
sessionId: this.currentSessionId,
|
|
3102
3128
|
conversationId: this.currentConversationId,
|
|
@@ -3129,6 +3155,7 @@ export class CortexOrchestrator {
|
|
|
3129
3155
|
...(gateConverted.usage && { usage: gateConverted.usage }),
|
|
3130
3156
|
};
|
|
3131
3157
|
this.messageHistory.push(gateAssistantMessage);
|
|
3158
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
3132
3159
|
await this.historyStore.appendMessage(this.currentSessionId, gateAssistantMessage);
|
|
3133
3160
|
currentAssistantMessage = gateAssistantMessage;
|
|
3134
3161
|
currentAssistantCanonicalMessage = gateAssistantCanonical;
|
|
@@ -3589,6 +3616,7 @@ export class CortexOrchestrator {
|
|
|
3589
3616
|
const block = lastMsg.message.content[0];
|
|
3590
3617
|
const existing = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
3591
3618
|
block.content = existing + '\n\n' + signals;
|
|
3619
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
3592
3620
|
// Observability (backlog item 3, micro-suite defect #4): this
|
|
3593
3621
|
// mutation happens AFTER the tool_result was persisted — the
|
|
3594
3622
|
// durable session lacks the steering the model saw. Bank a
|
|
@@ -3723,7 +3751,7 @@ export class CortexOrchestrator {
|
|
|
3723
3751
|
// are included in the continuation request's tools array
|
|
3724
3752
|
if (this.effectiveDeferredLoading && !isPTCEnabled) {
|
|
3725
3753
|
const beforeCount = toolsToUse.length;
|
|
3726
|
-
toolsToUse = this.toolFilter.getFilteredTools(allTools);
|
|
3754
|
+
toolsToUse = this.toolFilter.getFilteredTools(allTools, { appendOnly: resolveAppendOnlyTools() });
|
|
3727
3755
|
// StructuredOutput: the re-filter rebuilds from allTools (which never
|
|
3728
3756
|
// contained the request-scoped tool) — re-append so it stays present
|
|
3729
3757
|
// on every request of the turn.
|
|
@@ -4028,7 +4056,7 @@ export class CortexOrchestrator {
|
|
|
4028
4056
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
4029
4057
|
const synthEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param
|
|
4030
4058
|
const buildSynthRequest = () => {
|
|
4031
|
-
const synthCanonicalHistory = this.convertToCanonicalMessages(
|
|
4059
|
+
const synthCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
4032
4060
|
const req = this.gatewayTranslation.prepareRequest(synthCanonicalHistory,
|
|
4033
4061
|
// Tools suppressed — the model MUST produce text, not call more tools.
|
|
4034
4062
|
// NOTE (structured-output constraint, 2026-08-02): this is R29a's
|
|
@@ -4085,6 +4113,7 @@ export class CortexOrchestrator {
|
|
|
4085
4113
|
...(synthConverted.usage && { usage: synthConverted.usage }),
|
|
4086
4114
|
};
|
|
4087
4115
|
this.messageHistory.push(synthAssistantMessage);
|
|
4116
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
4088
4117
|
await this.historyStore.appendMessage(this.currentSessionId, synthAssistantMessage);
|
|
4089
4118
|
this.sessionTimeline.recordMessage(synthAssistantMessage.uuid, 'assistant');
|
|
4090
4119
|
if (synthConverted.usage) {
|
|
@@ -4487,6 +4516,7 @@ export class CortexOrchestrator {
|
|
|
4487
4516
|
}
|
|
4488
4517
|
};
|
|
4489
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
|
|
4490
4520
|
// canonicalHistory computed after effectiveModel is determined (input-slicing below).
|
|
4491
4521
|
// Tools already gathered above for middleware injection
|
|
4492
4522
|
// Server-side tool detection
|
|
@@ -4514,7 +4544,7 @@ export class CortexOrchestrator {
|
|
|
4514
4544
|
const isPTCEnabled = this.config.enablePTC && effectiveModel.supportsPTC && effectiveModel.provider === 'anthropic';
|
|
4515
4545
|
// Deferred loading: filter tools for non-PTC paths (essential + recently-used only)
|
|
4516
4546
|
if (!isPTCEnabled && this.effectiveDeferredLoading && toolsToUse && toolsToUse.length > 0) {
|
|
4517
|
-
toolsToUse = this.toolFilter.getFilteredTools(toolsToUse);
|
|
4547
|
+
toolsToUse = this.toolFilter.getFilteredTools(toolsToUse, { appendOnly: resolveAppendOnlyTools() });
|
|
4518
4548
|
}
|
|
4519
4549
|
// First-turn anchoring narrows further while armed (no-op once lifted).
|
|
4520
4550
|
// Card-level home door: capture the active card's anchorProfile (env
|
|
@@ -4909,6 +4939,7 @@ export class CortexOrchestrator {
|
|
|
4909
4939
|
if (isEmpty) {
|
|
4910
4940
|
m.message = m.message || { role: 'assistant' };
|
|
4911
4941
|
m.message.content = [{ type: 'text', text: '(no output)' }];
|
|
4942
|
+
this.invalidateConversions([m]); // R221 safety net
|
|
4912
4943
|
}
|
|
4913
4944
|
break;
|
|
4914
4945
|
}
|
|
@@ -4942,7 +4973,7 @@ export class CortexOrchestrator {
|
|
|
4942
4973
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
4943
4974
|
const retryEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param
|
|
4944
4975
|
const buildRetryRequest = () => {
|
|
4945
|
-
const retryCanonicalHistory = this.convertToCanonicalMessages(
|
|
4976
|
+
const retryCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
4946
4977
|
const req = this.gatewayTranslation.prepareRequest(retryCanonicalHistory, toolsToUse, effectiveModel, {
|
|
4947
4978
|
temperature: options.parameters?.temperature,
|
|
4948
4979
|
maxTokens: options.parameters?.maxTokens,
|
|
@@ -4991,6 +5022,7 @@ export class CortexOrchestrator {
|
|
|
4991
5022
|
...(retryConverted.usage && { usage: retryConverted.usage }),
|
|
4992
5023
|
};
|
|
4993
5024
|
this.messageHistory.push(retryAssistantMessage);
|
|
5025
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
4994
5026
|
await this.historyStore.appendMessage(this.currentSessionId, retryAssistantMessage);
|
|
4995
5027
|
currentAssistantCanonicalMessage = retryAssistantCanonical;
|
|
4996
5028
|
lastStopReason = retryConverted.stopReason; // R153
|
|
@@ -5068,28 +5100,28 @@ export class CortexOrchestrator {
|
|
|
5068
5100
|
await this.historyStore.appendMessage(this.currentSessionId, inactionUserMessage);
|
|
5069
5101
|
try {
|
|
5070
5102
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
5071
|
-
const
|
|
5072
|
-
const
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
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)
|
|
5093
5125
|
const retryConverted = this.gatewayTranslation.convertResponse(retryApiResponse.data, effectiveModel, {
|
|
5094
5126
|
sessionId: this.currentSessionId,
|
|
5095
5127
|
conversationId: this.currentConversationId,
|
|
@@ -5122,6 +5154,7 @@ export class CortexOrchestrator {
|
|
|
5122
5154
|
...(retryConverted.usage && { usage: retryConverted.usage }),
|
|
5123
5155
|
};
|
|
5124
5156
|
this.messageHistory.push(retryAssistantMessage);
|
|
5157
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
5125
5158
|
await this.historyStore.appendMessage(this.currentSessionId, retryAssistantMessage);
|
|
5126
5159
|
currentAssistantCanonicalMessage = retryAssistantCanonical;
|
|
5127
5160
|
lastStopReason = retryConverted.stopReason; // R153
|
|
@@ -5428,6 +5461,7 @@ export class CortexOrchestrator {
|
|
|
5428
5461
|
const block = lastMsg.message.content[0];
|
|
5429
5462
|
const existing = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
5430
5463
|
block.content = existing + '\n\n' + signals;
|
|
5464
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
5431
5465
|
// Observability (backlog item 3): post-persist mutation — bank a
|
|
5432
5466
|
// steering_injected event (streaming parity).
|
|
5433
5467
|
const store = this.getDecisionStore();
|
|
@@ -5544,7 +5578,7 @@ export class CortexOrchestrator {
|
|
|
5544
5578
|
}
|
|
5545
5579
|
// Re-filter tools after SearchTools discovery (same as sendMessage path)
|
|
5546
5580
|
if (this.effectiveDeferredLoading && !isPTCEnabled) {
|
|
5547
|
-
toolsToUse = this.toolFilter.getFilteredTools(allTools);
|
|
5581
|
+
toolsToUse = this.toolFilter.getFilteredTools(allTools, { appendOnly: resolveAppendOnlyTools() });
|
|
5548
5582
|
// StructuredOutput: re-filter rebuilds from allTools — re-append the
|
|
5549
5583
|
// request-scoped tool (same as the sendMessage continuation path).
|
|
5550
5584
|
if (structuredOutputState) {
|
|
@@ -5935,7 +5969,7 @@ export class CortexOrchestrator {
|
|
|
5935
5969
|
await this.ensureHistoryFitsModel(effectiveModel);
|
|
5936
5970
|
const synthEffort = this.consumeReasoningBackoff() ?? options.parameters?.reasoningEffort; // R153 backoff > request param
|
|
5937
5971
|
const buildSynthRequest = () => {
|
|
5938
|
-
const synthCanonicalHistory = this.convertToCanonicalMessages(
|
|
5972
|
+
const synthCanonicalHistory = this.convertToCanonicalMessages(this.chainedHistoryForApi(effectiveModel)); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = full history)
|
|
5939
5973
|
const req = this.gatewayTranslation.prepareRequest(synthCanonicalHistory, [], // tools suppressed — must produce text
|
|
5940
5974
|
effectiveModel, {
|
|
5941
5975
|
temperature: options.parameters?.temperature,
|
|
@@ -5989,6 +6023,7 @@ export class CortexOrchestrator {
|
|
|
5989
6023
|
...(synthConverted.usage && { usage: synthConverted.usage }),
|
|
5990
6024
|
};
|
|
5991
6025
|
this.messageHistory.push(synthAssistantMessage);
|
|
6026
|
+
this.markChainedResponseCheckpoint(effectiveModel); // R228 (CORTEX_RESPONSES_SLICE_ALL; off = no-op)
|
|
5992
6027
|
await this.historyStore.appendMessage(this.currentSessionId, synthAssistantMessage);
|
|
5993
6028
|
this.sessionTimeline.recordMessage(synthAssistantMessage.uuid, 'assistant');
|
|
5994
6029
|
currentAssistantCanonicalMessage = synthCanonical;
|
|
@@ -6338,6 +6373,7 @@ export class CortexOrchestrator {
|
|
|
6338
6373
|
// cleared on session reset so stale entries (including synthetic-repair
|
|
6339
6374
|
// uuids from the previous session) can't leak into the new session.
|
|
6340
6375
|
this.canonicalConversionCache.clear();
|
|
6376
|
+
this.injectedApiContent.clear(); // R223
|
|
6341
6377
|
// Reset XAI Responses API chain for new session (was leaking across `--new`).
|
|
6342
6378
|
// Without this, a new session would reuse lastResponseId from the previous
|
|
6343
6379
|
// session, sending previous_response_id that the server doesn't recognize
|
|
@@ -6467,6 +6503,7 @@ export class CortexOrchestrator {
|
|
|
6467
6503
|
// Round 6: clear canonical-conversion cache on session resume too —
|
|
6468
6504
|
// entries from a previously running orchestrator instance are stale.
|
|
6469
6505
|
this.canonicalConversionCache.clear();
|
|
6506
|
+
this.injectedApiContent.clear(); // R223
|
|
6470
6507
|
// Reuse the persistedMetadata loaded above — avoid a second disk read.
|
|
6471
6508
|
const savedMetadata = persistedMetadata;
|
|
6472
6509
|
if (savedMetadata?.cacheMetrics) {
|
|
@@ -6937,6 +6974,7 @@ export class CortexOrchestrator {
|
|
|
6937
6974
|
plateau: plateauStatusSegment(this.plateauTracker), // R210: null when CORTEX_PLATEAU_STOP is off or no metric seen → line unchanged
|
|
6938
6975
|
});
|
|
6939
6976
|
if (appendStatusToNewestToolResult(this.messageHistory, line)) {
|
|
6977
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
6940
6978
|
this.turnStatusLines++;
|
|
6941
6979
|
if (this.config.debug)
|
|
6942
6980
|
console.log(`[TurnStatus] iteration ${iteration}: ${line}`);
|
|
@@ -9388,7 +9426,17 @@ export class CortexOrchestrator {
|
|
|
9388
9426
|
try {
|
|
9389
9427
|
if (!isReasoningExhaustion(cls) || !resolveWallDrop())
|
|
9390
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 };
|
|
9391
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]);
|
|
9392
9440
|
const store = this.getDecisionStore();
|
|
9393
9441
|
if (r.dropped && store)
|
|
9394
9442
|
void store.recordEvent({ sessionId: this.currentSessionId ?? 'unknown', kind: 'wall_drop', detail: { iteration, droppedChars: r.droppedChars, carrier: r.carrier, streaming } }).catch(() => { });
|
|
@@ -9413,6 +9461,7 @@ export class CortexOrchestrator {
|
|
|
9413
9461
|
return;
|
|
9414
9462
|
if (!appendStatusToNewestToolResult(this.messageHistory, buildComputeNudgeLine(reasoningTokens)))
|
|
9415
9463
|
return;
|
|
9464
|
+
this.invalidateTailUnitConversions(); // R221 safety net
|
|
9416
9465
|
this.computeNudgeLastRound = round;
|
|
9417
9466
|
this.computeNudgeFired++;
|
|
9418
9467
|
const store = this.getDecisionStore();
|
|
@@ -9454,7 +9503,7 @@ export class CortexOrchestrator {
|
|
|
9454
9503
|
? (await this.retryMiddleware.executeWithRetry(() => this.apiClient.sendRequest(req, model), label)).result
|
|
9455
9504
|
: this.apiClient.sendRequest(req, model);
|
|
9456
9505
|
try {
|
|
9457
|
-
return await send(build())
|
|
9506
|
+
return await this.sendWithChainFallback(build(), send, build, label); // R228 fallback (dark; off = send(build()))
|
|
9458
9507
|
}
|
|
9459
9508
|
catch (err) {
|
|
9460
9509
|
if (!isImageRejectionError(err))
|
|
@@ -9463,6 +9512,7 @@ export class CortexOrchestrator {
|
|
|
9463
9512
|
const stripped = stripRejectedImages(this.messageHistory, reason);
|
|
9464
9513
|
if (stripped === 0)
|
|
9465
9514
|
throw err;
|
|
9515
|
+
this.invalidateConversions(this.messageHistory); // R221 safety net: stubbed image blocks live in already-converted messages
|
|
9466
9516
|
console.warn(`[Orchestrator] R154: provider rejected an image block on ${label} — stubbed ${stripped} image block(s) in history and retrying once.`);
|
|
9467
9517
|
const store = this.getDecisionStore();
|
|
9468
9518
|
if (store)
|
|
@@ -9970,6 +10020,11 @@ export class CortexOrchestrator {
|
|
|
9970
10020
|
* so this cleanup just removes them from the in-memory array.
|
|
9971
10021
|
*/
|
|
9972
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;
|
|
9973
10028
|
const beforeCount = this.messageHistory.length;
|
|
9974
10029
|
this.messageHistory = this.messageHistory.filter((msg) => !msg.metadata?.ephemeral);
|
|
9975
10030
|
const removedCount = beforeCount - this.messageHistory.length;
|
|
@@ -10362,7 +10417,78 @@ export class CortexOrchestrator {
|
|
|
10362
10417
|
* Also validates and repairs the message history to handle crash recovery scenarios
|
|
10363
10418
|
* where tool_use blocks might be orphaned without matching tool_results.
|
|
10364
10419
|
*/
|
|
10365
|
-
|
|
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;
|
|
10366
10492
|
const tailIdx = messages.length - 1;
|
|
10367
10493
|
const converted = messages.map((msg, i) => {
|
|
10368
10494
|
// Tail is recomputed every call (see cache doc-comment above).
|