@nexus-cortex/core 4.84.0 → 4.86.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/dist/adapters/GatewayTranslationLayer.d.ts.map +1 -1
- package/dist/adapters/GatewayTranslationLayer.js +11 -2
- package/dist/adapters/GatewayTranslationLayer.js.map +1 -1
- package/dist/models/cards/deepseek/deepseek-v4-flash-vision-exp.d.ts.map +1 -1
- package/dist/models/cards/deepseek/deepseek-v4-flash-vision-exp.js +5 -0
- package/dist/models/cards/deepseek/deepseek-v4-flash-vision-exp.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 +7 -0
- package/dist/models/cards/deepseek/deepseek-v4-flash.js.map +1 -1
- package/dist/models/cards/deepseek/deepseek-v4-pro.d.ts.map +1 -1
- package/dist/models/cards/deepseek/deepseek-v4-pro.js +9 -0
- package/dist/models/cards/deepseek/deepseek-v4-pro.js.map +1 -1
- package/dist/models/configurators/DeepSeekConfigurator.d.ts +8 -0
- package/dist/models/configurators/DeepSeekConfigurator.d.ts.map +1 -1
- package/dist/models/configurators/DeepSeekConfigurator.js +3 -0
- package/dist/models/configurators/DeepSeekConfigurator.js.map +1 -1
- package/dist/orchestrator/APIClient.d.ts.map +1 -1
- package/dist/orchestrator/APIClient.js +17 -1
- package/dist/orchestrator/APIClient.js.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.d.ts +1 -2
- package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.js +48 -36
- package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
- package/dist/tools/ToolProfile.d.ts +9 -0
- package/dist/tools/ToolProfile.d.ts.map +1 -1
- package/dist/tools/ToolProfile.js +37 -0
- package/dist/tools/ToolProfile.js.map +1 -1
- package/package.json +3 -3
|
@@ -30,7 +30,7 @@ import { CacheMetricsAccumulator } from '../session/CacheMetricsAccumulator.js';
|
|
|
30
30
|
import { SubAgentProcessManager, createSubAgentProcessManager } from './SubAgentProcessManager.js';
|
|
31
31
|
// Phase 1: Tool Architecture Refactor - Unified Tool Registry
|
|
32
32
|
import { toolFactory } from '../tools/ToolFactory.js';
|
|
33
|
-
import { resolveToolProfile, resolveToolAnchor, resolveFrameProfile, isNarrowProfile, isToolAllowedByProfile, applyToolProfile } from '../tools/ToolProfile.js';
|
|
33
|
+
import { resolveToolProfile, resolveToolAnchor, resolveFrameProfile, resolveLiftNudge, resolveHeadlessDropAskUser, resolveDeferredLoading, isNarrowProfile, isToolAllowedByProfile, applyToolProfile } from '../tools/ToolProfile.js';
|
|
34
34
|
import { readStagedDoctrine, applyCuratedDoctrine, runOrientForStaging, withTimeout } from './doctrineCuration.js';
|
|
35
35
|
import { ExactRepeatTracker } from '../training/loopLadder.js';
|
|
36
36
|
import { slashCommandRegistry } from '../commands/SlashCommandRegistry.js';
|
|
@@ -163,6 +163,7 @@ export class CortexOrchestrator {
|
|
|
163
163
|
* exactly once, at the anchor-lift boundary. One-shot per orchestrator. */
|
|
164
164
|
deferredCorpusDelivered = false;
|
|
165
165
|
liftNudgeDelivered = false; // A′ proposal-1: lift-boundary SearchTools/AskForAdvice nudge, one-shot
|
|
166
|
+
effectiveDeferredLoading = true; // per-turn resolved deferred-loading (card > env > settings); set at assembly
|
|
166
167
|
/**
|
|
167
168
|
* Item 10 — helper-curated doctrine freshness (operator-designed; docs/
|
|
168
169
|
* HARNESS_IMPROVEMENT_BACKLOG.md). SYNCHRONOUS-BY-BOUNDARY: under defer the
|
|
@@ -272,11 +273,6 @@ export class CortexOrchestrator {
|
|
|
272
273
|
console.warn(`[Anchor] deferred corpus delivery failed (continuing without): ${e?.message ?? e}`);
|
|
273
274
|
}
|
|
274
275
|
}
|
|
275
|
-
/** Truthy check for a raw env flag (mirrors the CORTEX_PROMPT_MASS parse idiom). */
|
|
276
|
-
envOn(v) {
|
|
277
|
-
const s = (v ?? '').trim().toLowerCase();
|
|
278
|
-
return s === '1' || s === 'true' || s === 'yes' || s === 'on';
|
|
279
|
-
}
|
|
280
276
|
/** A′ proposal-2: in a NON-INTERACTIVE session (stateless /v1/messages, headless
|
|
281
277
|
* API, piped oneshot) there is no human to answer AskUserQuestion — it becomes a
|
|
282
278
|
* stall trap. When CORTEX_HEADLESS_DROP_ASKUSER is truthy AND the session is
|
|
@@ -284,9 +280,9 @@ export class CortexOrchestrator {
|
|
|
284
280
|
* (deferred filter, anchor, lift rebuild, re-filter) excludes it. Kept in
|
|
285
281
|
* interactive TUIs (a real user can answer). Headless swaps the human-ask for the
|
|
286
282
|
* model-ask (AskForAdvice, which the lift nudge signposts). Gated + idempotent. */
|
|
287
|
-
stripHeadlessOnlyTools(tools) {
|
|
288
|
-
if (!
|
|
289
|
-
return tools;
|
|
283
|
+
stripHeadlessOnlyTools(tools, model) {
|
|
284
|
+
if (!resolveHeadlessDropAskUser(process.env, model?.headlessDropAskUser))
|
|
285
|
+
return tools; // card > env > false
|
|
290
286
|
if (!this.approvalMode.autoApproveActions)
|
|
291
287
|
return tools; // interactive: keep it
|
|
292
288
|
return tools.filter((t) => t.name !== 'AskUserQuestion');
|
|
@@ -299,12 +295,12 @@ export class CortexOrchestrator {
|
|
|
299
295
|
* AFTER the model has committed to acting (post-lift is where prompt mass is safe —
|
|
300
296
|
* this is a one-line signpost, NOT defer's full corpus). Fires at most once.
|
|
301
297
|
* Mirrors deliverDeferredCorpusAtLift's append-to-last-tool_result mechanism. */
|
|
302
|
-
deliverLiftNudge(allTools) {
|
|
298
|
+
deliverLiftNudge(allTools, model) {
|
|
303
299
|
if (this.liftNudgeDelivered)
|
|
304
300
|
return;
|
|
305
|
-
if (!
|
|
306
|
-
return;
|
|
307
|
-
if (!this.
|
|
301
|
+
if (!resolveLiftNudge(process.env, model?.liftNudge))
|
|
302
|
+
return; // card > env > false
|
|
303
|
+
if (!this.effectiveDeferredLoading)
|
|
308
304
|
return; // deferred off ⇒ no hidden tools to point at
|
|
309
305
|
this.liftNudgeDelivered = true; // one-shot even if assembly fails
|
|
310
306
|
try {
|
|
@@ -441,7 +437,7 @@ export class CortexOrchestrator {
|
|
|
441
437
|
// The closure captures `this` so it resolves the active model's naming
|
|
442
438
|
// convention at call-time — names in SearchTools results match the tools
|
|
443
439
|
// array the model sees (e.g. snake_case for XAI, PascalCase for Anthropic).
|
|
444
|
-
if (this.config.enableDeferredToolLoading) {
|
|
440
|
+
if (this.config.enableDeferredToolLoading) { // constructor setup — base capability, not per-turn
|
|
445
441
|
const searchExecutor = this.executorRegistry.getExecutor('SearchTools');
|
|
446
442
|
if (searchExecutor && typeof searchExecutor.setToolProvider === 'function') {
|
|
447
443
|
const namingHandler = new ToolNamingHandler();
|
|
@@ -480,7 +476,7 @@ export class CortexOrchestrator {
|
|
|
480
476
|
console.log(`[Orchestrator] Dependency injection pattern - all components injected`);
|
|
481
477
|
console.log(`[Orchestrator] ExecutorRegistry: ${this.executorRegistry.getExecutorCount()} executors`);
|
|
482
478
|
console.log(`[Orchestrator] MCP enabled: ${mcpManager !== null}`);
|
|
483
|
-
if (this.config.enableDeferredToolLoading) {
|
|
479
|
+
if (this.config.enableDeferredToolLoading) { // constructor debug log
|
|
484
480
|
console.log(`[Orchestrator] Deferred tool loading: ENABLED`);
|
|
485
481
|
}
|
|
486
482
|
}
|
|
@@ -613,11 +609,16 @@ export class CortexOrchestrator {
|
|
|
613
609
|
const mcpTools = this.mcpAutoInject && !bashOnlyProfile ? this.getMcpToolsAsCanonical() : [];
|
|
614
610
|
const mcpManagementTools = bashOnlyProfile ? [] : this.getMcpManagementTools();
|
|
615
611
|
const contextManagementTools = bashOnlyProfile ? [] : this.getContextManagementTools();
|
|
612
|
+
// Per-model deferred-loading resolved ONCE per turn (card > env > settings), so every per-request
|
|
613
|
+
// deferred gate below flips together — this is what makes the pro card's deferredToolLoading:false
|
|
614
|
+
// toggle (or a global env) coherent instead of split-brain. Constructor SearchTools wiring keeps
|
|
615
|
+
// this.config (base capability, pre-turn).
|
|
616
|
+
this.effectiveDeferredLoading = resolveDeferredLoading(process.env, model?.deferredToolLoading, this.config.enableDeferredToolLoading);
|
|
616
617
|
// Include all default tools by default, or custom tools if provided
|
|
617
618
|
// A′ proposal-2: strip AskUserQuestion in non-interactive sessions (gated) at the source.
|
|
618
619
|
const allTools = this.stripHeadlessOnlyTools(options.tools !== undefined
|
|
619
620
|
? [...factoryTools, ...mcpTools, ...mcpManagementTools, ...contextManagementTools, ...options.tools]
|
|
620
|
-
: [...factoryTools, ...mcpTools, ...mcpManagementTools, ...contextManagementTools]);
|
|
621
|
+
: [...factoryTools, ...mcpTools, ...mcpManagementTools, ...contextManagementTools], model);
|
|
621
622
|
// Phase 2.3: Inject server-side tools when enabled and model supports them
|
|
622
623
|
// R20: provider-aware — XAI gets web/x/code; OpenAI gets web/code/file/image
|
|
623
624
|
// (Responses API hosted tools); other providers stay client-only.
|
|
@@ -726,7 +727,7 @@ export class CortexOrchestrator {
|
|
|
726
727
|
}
|
|
727
728
|
// Deferred tool loading: categorized announcement with descriptions so
|
|
728
729
|
// the model can match user intent to tool names without needing full schemas.
|
|
729
|
-
if (this.
|
|
730
|
+
if (this.effectiveDeferredLoading && Array.isArray(injectedContent)) {
|
|
730
731
|
const convention = model.tools?.namingConvention || 'PascalCase';
|
|
731
732
|
const announcement = this.buildDeferredToolAnnouncement(convention);
|
|
732
733
|
if (announcement) {
|
|
@@ -850,7 +851,7 @@ export class CortexOrchestrator {
|
|
|
850
851
|
// Check if PTC should be used for this request
|
|
851
852
|
const isPTCEnabled = this.config.enablePTC && effectiveModel.supportsPTC && effectiveModel.provider === 'anthropic';
|
|
852
853
|
// Deferred loading: filter tools for non-PTC paths (essential + recently-used only)
|
|
853
|
-
if (!isPTCEnabled && this.
|
|
854
|
+
if (!isPTCEnabled && this.effectiveDeferredLoading && toolsToUse && toolsToUse.length > 0) {
|
|
854
855
|
toolsToUse = this.toolFilter.getFilteredTools(toolsToUse);
|
|
855
856
|
}
|
|
856
857
|
// First-turn anchoring narrows further while armed (no-op once lifted).
|
|
@@ -861,7 +862,7 @@ export class CortexOrchestrator {
|
|
|
861
862
|
// Thread the CARD's anchor to the executors (per-orchestrator config, by
|
|
862
863
|
// reference) so framing-aware executor behavior (ShellTool steering
|
|
863
864
|
// default) is model-card/registry-scoped, not process-global env.
|
|
864
|
-
this.executorRegistry.updateConfig({ activeAnchorProfile: this.cardAnchorProfile });
|
|
865
|
+
this.executorRegistry.updateConfig({ activeAnchorProfile: this.cardAnchorProfile, allowCommandSubstitution: this.approvalMode.autoApproveActions });
|
|
865
866
|
if (toolsToUse && toolsToUse.length > 0) {
|
|
866
867
|
toolsToUse = this.applyAnchorIfArmed(toolsToUse);
|
|
867
868
|
}
|
|
@@ -886,7 +887,7 @@ export class CortexOrchestrator {
|
|
|
886
887
|
// Reset sequential call counter at start of each user turn
|
|
887
888
|
this.mentorshipMiddleware?.resetSequentialCalls(this.currentSessionId);
|
|
888
889
|
// §13-B2: force AskForAdvice this turn on high-confidence thrash (mentor-force armed).
|
|
889
|
-
const forcedMentorChoice = await this.resolveForcedMentorChoice(toolsToUse);
|
|
890
|
+
const forcedMentorChoice = await this.resolveForcedMentorChoice(toolsToUse, 0); // initial request: 0 tool calls yet
|
|
890
891
|
const preparedRequest = this.gatewayTranslation.prepareRequest(canonicalHistory, toolsToUse, effectiveModel, {
|
|
891
892
|
temperature: options.parameters?.temperature,
|
|
892
893
|
maxTokens: options.parameters?.maxTokens,
|
|
@@ -2116,7 +2117,7 @@ export class CortexOrchestrator {
|
|
|
2116
2117
|
// Mark this tool_use_id as processed
|
|
2117
2118
|
processedToolUseIds.add(toolResult.tool_use_id);
|
|
2118
2119
|
// Progressive filter: record tool use for deferred loading
|
|
2119
|
-
if (this.
|
|
2120
|
+
if (this.effectiveDeferredLoading) {
|
|
2120
2121
|
this.toolFilter.recordToolUse(toolResult.tool_name);
|
|
2121
2122
|
// Record discovered tools from SearchTools results
|
|
2122
2123
|
if (toolResult.tool_name === 'SearchTools' && !toolResult.is_error) {
|
|
@@ -2307,7 +2308,7 @@ export class CortexOrchestrator {
|
|
|
2307
2308
|
// surface — never lift, never deliver the deferred corpus.
|
|
2308
2309
|
resolveFrameProfile(process.env, this.cardFrameProfile) !== 'persist') {
|
|
2309
2310
|
this.anchorLifted = true;
|
|
2310
|
-
if (!(this.
|
|
2311
|
+
if (!(this.effectiveDeferredLoading && !isPTCEnabled)) {
|
|
2311
2312
|
toolsToUse = allTools;
|
|
2312
2313
|
if (structuredOutputState) {
|
|
2313
2314
|
toolsToUse = ensureStructuredOutputTool(toolsToUse, structuredOutputState);
|
|
@@ -2316,13 +2317,13 @@ export class CortexOrchestrator {
|
|
|
2316
2317
|
// P6 deferral: the static corpus arrives at the same boundary as the
|
|
2317
2318
|
// full tool catalog (no-op unless CORTEX_PROMPT_MASS=defer).
|
|
2318
2319
|
await this.deliverDeferredCorpusAtLift(effectiveModel);
|
|
2319
|
-
this.deliverLiftNudge(allTools); // A′ proposal-1: SearchTools/AskForAdvice signpost at lift
|
|
2320
|
+
this.deliverLiftNudge(allTools, effectiveModel); // A′ proposal-1: SearchTools/AskForAdvice signpost at lift
|
|
2320
2321
|
if (this.config.debug)
|
|
2321
2322
|
console.log('[Anchor] lifted at first tool_result boundary — session profile applies');
|
|
2322
2323
|
}
|
|
2323
2324
|
// Re-filter tools after SearchTools discovery so newly discovered tools
|
|
2324
2325
|
// are included in the continuation request's tools array
|
|
2325
|
-
if (this.
|
|
2326
|
+
if (this.effectiveDeferredLoading && !isPTCEnabled) {
|
|
2326
2327
|
const beforeCount = toolsToUse.length;
|
|
2327
2328
|
toolsToUse = this.toolFilter.getFilteredTools(allTools);
|
|
2328
2329
|
// StructuredOutput: the re-filter rebuilds from allTools (which never
|
|
@@ -2347,6 +2348,10 @@ export class CortexOrchestrator {
|
|
|
2347
2348
|
? this.messageHistory.slice(this.messageCountAtLastResponse)
|
|
2348
2349
|
: this.messageHistory;
|
|
2349
2350
|
const continuationCanonicalHistory = this.convertToCanonicalMessages([...continuationHistorySource]);
|
|
2351
|
+
// §13-B2: re-evaluate the forced mentor choice on THIS continuation — thrash develops HERE
|
|
2352
|
+
// (turnNumber ≥ 1 with accumulating failing outcomes), NOT on the initial request (turnNumber 0
|
|
2353
|
+
// where the window is empty). Attaching it only to the initial assembly made the force unreachable.
|
|
2354
|
+
const continuationForcedChoice = await this.resolveForcedMentorChoice(toolsToUse, toolCallIteration);
|
|
2350
2355
|
const continuationRequest = this.gatewayTranslation.prepareRequest(continuationCanonicalHistory, toolsToUse, effectiveModel, {
|
|
2351
2356
|
temperature: options.parameters?.temperature,
|
|
2352
2357
|
maxTokens: options.parameters?.maxTokens,
|
|
@@ -2354,7 +2359,8 @@ export class CortexOrchestrator {
|
|
|
2354
2359
|
reasoningEffort: options.parameters?.reasoningEffort, // GPT-5.1 reasoning level
|
|
2355
2360
|
stream: options.streaming,
|
|
2356
2361
|
staticSystemPrompt: this.currentStaticSystemPrompt, // R28
|
|
2357
|
-
conversationId: this.currentConversationId // R28b
|
|
2362
|
+
conversationId: this.currentConversationId, // R28b
|
|
2363
|
+
toolChoice: continuationForcedChoice // §13-B2 forced mentor tool_choice (continuation)
|
|
2358
2364
|
});
|
|
2359
2365
|
// Stateful Responses API: chain with previous_response_id for server-side reasoning preservation
|
|
2360
2366
|
if (this.lastResponseId && effectiveModel.api.pattern === 'responses') {
|
|
@@ -2919,10 +2925,12 @@ export class CortexOrchestrator {
|
|
|
2919
2925
|
const mcpTools = this.mcpAutoInject && !bashOnlyProfile2 ? this.getMcpToolsAsCanonical() : [];
|
|
2920
2926
|
const mcpManagementTools = bashOnlyProfile2 ? [] : this.getMcpManagementTools();
|
|
2921
2927
|
const contextManagementTools = bashOnlyProfile2 ? [] : this.getContextManagementTools();
|
|
2928
|
+
// Per-model deferred-loading resolved once per turn (card > env > settings) — streaming mirror.
|
|
2929
|
+
this.effectiveDeferredLoading = resolveDeferredLoading(process.env, model?.deferredToolLoading, this.config.enableDeferredToolLoading);
|
|
2922
2930
|
// A′ proposal-2: strip AskUserQuestion in non-interactive sessions (gated) at the source.
|
|
2923
2931
|
const allTools = this.stripHeadlessOnlyTools(options.tools !== undefined
|
|
2924
2932
|
? [...factoryTools, ...mcpTools, ...mcpManagementTools, ...contextManagementTools, ...options.tools]
|
|
2925
|
-
: [...factoryTools, ...mcpTools, ...mcpManagementTools, ...contextManagementTools]);
|
|
2933
|
+
: [...factoryTools, ...mcpTools, ...mcpManagementTools, ...contextManagementTools], model);
|
|
2926
2934
|
// Phase 2.3: Inject server-side tools when enabled and model supports them
|
|
2927
2935
|
// R20: provider-aware (streaming path mirror of non-streaming injection above).
|
|
2928
2936
|
if (isServerSideToolsEnabled() && modelSupportsServerSideTools(model)) {
|
|
@@ -3019,7 +3027,7 @@ export class CortexOrchestrator {
|
|
|
3019
3027
|
}
|
|
3020
3028
|
// Deferred tool loading: categorized announcement with descriptions so
|
|
3021
3029
|
// the model can match user intent to tool names without needing full schemas.
|
|
3022
|
-
if (this.
|
|
3030
|
+
if (this.effectiveDeferredLoading && Array.isArray(injectedContent)) {
|
|
3023
3031
|
const convention = model.tools?.namingConvention || 'PascalCase';
|
|
3024
3032
|
const announcement = this.buildDeferredToolAnnouncement(convention);
|
|
3025
3033
|
if (announcement) {
|
|
@@ -3097,7 +3105,7 @@ export class CortexOrchestrator {
|
|
|
3097
3105
|
// Check if PTC should be used for this request (streaming)
|
|
3098
3106
|
const isPTCEnabled = this.config.enablePTC && effectiveModel.supportsPTC && effectiveModel.provider === 'anthropic';
|
|
3099
3107
|
// Deferred loading: filter tools for non-PTC paths (essential + recently-used only)
|
|
3100
|
-
if (!isPTCEnabled && this.
|
|
3108
|
+
if (!isPTCEnabled && this.effectiveDeferredLoading && toolsToUse && toolsToUse.length > 0) {
|
|
3101
3109
|
toolsToUse = this.toolFilter.getFilteredTools(toolsToUse);
|
|
3102
3110
|
}
|
|
3103
3111
|
// First-turn anchoring narrows further while armed (no-op once lifted).
|
|
@@ -3108,7 +3116,7 @@ export class CortexOrchestrator {
|
|
|
3108
3116
|
// Thread the CARD's anchor to the executors (per-orchestrator config, by
|
|
3109
3117
|
// reference) so framing-aware executor behavior (ShellTool steering
|
|
3110
3118
|
// default) is model-card/registry-scoped, not process-global env.
|
|
3111
|
-
this.executorRegistry.updateConfig({ activeAnchorProfile: this.cardAnchorProfile });
|
|
3119
|
+
this.executorRegistry.updateConfig({ activeAnchorProfile: this.cardAnchorProfile, allowCommandSubstitution: this.approvalMode.autoApproveActions });
|
|
3112
3120
|
if (toolsToUse && toolsToUse.length > 0) {
|
|
3113
3121
|
toolsToUse = this.applyAnchorIfArmed(toolsToUse);
|
|
3114
3122
|
}
|
|
@@ -3143,7 +3151,7 @@ export class CortexOrchestrator {
|
|
|
3143
3151
|
console.log(`[Orchestrator Streaming] Input-sliced initial for cross-turn chain: sent ${initialHistoryForApiStreaming.length}/${messageHistoryForApi.length} messages`);
|
|
3144
3152
|
}
|
|
3145
3153
|
// §13-B2: force AskForAdvice this turn on high-confidence thrash (mentor-force armed).
|
|
3146
|
-
const forcedMentorChoice = await this.resolveForcedMentorChoice(toolsToUse);
|
|
3154
|
+
const forcedMentorChoice = await this.resolveForcedMentorChoice(toolsToUse, 0); // initial request: 0 tool calls yet
|
|
3147
3155
|
// Prepare request
|
|
3148
3156
|
const preparedRequest = this.gatewayTranslation.prepareRequest(canonicalHistory, toolsToUse, effectiveModel, {
|
|
3149
3157
|
temperature: options.parameters?.temperature,
|
|
@@ -3766,7 +3774,7 @@ export class CortexOrchestrator {
|
|
|
3766
3774
|
// Mark this tool_use_id as processed to prevent duplicate in error handler
|
|
3767
3775
|
processedToolUseIds.add(toolResult.tool_use_id);
|
|
3768
3776
|
// Progressive filter: record tool use for deferred loading (streaming)
|
|
3769
|
-
if (this.
|
|
3777
|
+
if (this.effectiveDeferredLoading) {
|
|
3770
3778
|
this.toolFilter.recordToolUse(toolResult.tool_name);
|
|
3771
3779
|
if (toolResult.tool_name === 'SearchTools' && !toolResult.is_error) {
|
|
3772
3780
|
const content = typeof toolResult.content === 'string'
|
|
@@ -3932,7 +3940,7 @@ export class CortexOrchestrator {
|
|
|
3932
3940
|
// surface — never lift, never deliver the deferred corpus.
|
|
3933
3941
|
resolveFrameProfile(process.env, this.cardFrameProfile) !== 'persist') {
|
|
3934
3942
|
this.anchorLifted = true;
|
|
3935
|
-
if (!(this.
|
|
3943
|
+
if (!(this.effectiveDeferredLoading && !isPTCEnabled)) {
|
|
3936
3944
|
toolsToUse = allTools;
|
|
3937
3945
|
if (structuredOutputState) {
|
|
3938
3946
|
toolsToUse = ensureStructuredOutputTool(toolsToUse, structuredOutputState);
|
|
@@ -3940,12 +3948,12 @@ export class CortexOrchestrator {
|
|
|
3940
3948
|
}
|
|
3941
3949
|
// P6 deferral: same one-shot corpus delivery as the sendMessage path.
|
|
3942
3950
|
await this.deliverDeferredCorpusAtLift(effectiveModel);
|
|
3943
|
-
this.deliverLiftNudge(allTools); // A′ proposal-1: SearchTools/AskForAdvice signpost at lift
|
|
3951
|
+
this.deliverLiftNudge(allTools, effectiveModel); // A′ proposal-1: SearchTools/AskForAdvice signpost at lift
|
|
3944
3952
|
if (this.config.debug)
|
|
3945
3953
|
console.log('[Anchor] lifted at first tool_result boundary — session profile applies');
|
|
3946
3954
|
}
|
|
3947
3955
|
// Re-filter tools after SearchTools discovery (same as sendMessage path)
|
|
3948
|
-
if (this.
|
|
3956
|
+
if (this.effectiveDeferredLoading && !isPTCEnabled) {
|
|
3949
3957
|
toolsToUse = this.toolFilter.getFilteredTools(allTools);
|
|
3950
3958
|
// StructuredOutput: re-filter rebuilds from allTools — re-append the
|
|
3951
3959
|
// request-scoped tool (same as the sendMessage continuation path).
|
|
@@ -3962,6 +3970,9 @@ export class CortexOrchestrator {
|
|
|
3962
3970
|
? this.messageHistory.slice(this.messageCountAtLastResponse)
|
|
3963
3971
|
: this.messageHistory;
|
|
3964
3972
|
const continuationCanonicalHistory = this.convertToCanonicalMessages([...streamContinuationHistorySource]);
|
|
3973
|
+
// §13-B2: re-evaluate the forced mentor choice on THIS streaming continuation — thrash
|
|
3974
|
+
// develops here, not on the initial request (mirror of the non-stream continuation fix).
|
|
3975
|
+
const continuationForcedChoice = await this.resolveForcedMentorChoice(toolsToUse, toolCallIteration);
|
|
3965
3976
|
// Phase 2.8: Prepare continuation request
|
|
3966
3977
|
// All providers maintain thinking throughout multi-turn execution
|
|
3967
3978
|
const continuationRequest = this.gatewayTranslation.prepareRequest(continuationCanonicalHistory, toolsToUse, // Updated tools including SearchTools discoveries
|
|
@@ -3973,6 +3984,7 @@ export class CortexOrchestrator {
|
|
|
3973
3984
|
stream: true, // STREAMING continuation
|
|
3974
3985
|
staticSystemPrompt: this.currentStaticSystemPrompt, // R28
|
|
3975
3986
|
conversationId: this.currentConversationId, // R28b
|
|
3987
|
+
toolChoice: continuationForcedChoice // §13-B2 forced mentor tool_choice (continuation)
|
|
3976
3988
|
// Thinking enabled for all providers in continuations
|
|
3977
3989
|
// This allows interleaved thinking during tool execution
|
|
3978
3990
|
});
|
|
@@ -6812,7 +6824,7 @@ export class CortexOrchestrator {
|
|
|
6812
6824
|
* of mostly-failures, currently failing). Returns the CANONICAL forced choice; the
|
|
6813
6825
|
* gateway converts its name to the wire form. `tool_choice` is body-level → cache-safe.
|
|
6814
6826
|
*/
|
|
6815
|
-
async resolveForcedMentorChoice(toolsToUse) {
|
|
6827
|
+
async resolveForcedMentorChoice(toolsToUse, turnCount) {
|
|
6816
6828
|
if (!this.config.reactiveMentorship?.enabled)
|
|
6817
6829
|
return undefined;
|
|
6818
6830
|
if (process.env.CORTEX_MENTOR_FORCE !== 'true')
|
|
@@ -6827,7 +6839,7 @@ export class CortexOrchestrator {
|
|
|
6827
6839
|
return undefined;
|
|
6828
6840
|
try {
|
|
6829
6841
|
const outcomes = await store.recentOutcomes(resolveThrashConfig().window);
|
|
6830
|
-
return resolveThrashState(outcomes,
|
|
6842
|
+
return resolveThrashState(outcomes, turnCount).thrashing
|
|
6831
6843
|
? { type: 'tool', name: 'AskForAdvice' }
|
|
6832
6844
|
: undefined;
|
|
6833
6845
|
}
|