@nexus-cortex/core 4.89.0 → 4.90.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +76 -3
- package/dist/config/RuntimeConfigRegistry.d.ts.map +1 -1
- package/dist/config/RuntimeConfigRegistry.js +2 -0
- package/dist/config/RuntimeConfigRegistry.js.map +1 -1
- package/dist/config/SettingsLoader.d.ts.map +1 -1
- package/dist/config/SettingsLoader.js +2 -0
- package/dist/config/SettingsLoader.js.map +1 -1
- package/dist/config/SettingsSchema.d.ts +4 -0
- package/dist/config/SettingsSchema.d.ts.map +1 -1
- package/dist/config/SettingsSchema.js +19 -0
- package/dist/config/SettingsSchema.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +3 -1
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/middleware/HelperModelMiddleware.d.ts +13 -0
- package/dist/middleware/HelperModelMiddleware.d.ts.map +1 -1
- package/dist/middleware/HelperModelMiddleware.js +32 -0
- package/dist/middleware/HelperModelMiddleware.js.map +1 -1
- package/dist/middleware/helpers/HelperMiddlewareAdapter.interface.d.ts +6 -1
- package/dist/middleware/helpers/HelperMiddlewareAdapter.interface.d.ts.map +1 -1
- package/dist/middleware/helpers/HelperMiddlewareAdapter.interface.js.map +1 -1
- package/dist/middleware/helpers/adapters/ChatCompletionsAPIHelperAdapter.d.ts +9 -1
- package/dist/middleware/helpers/adapters/ChatCompletionsAPIHelperAdapter.d.ts.map +1 -1
- package/dist/middleware/helpers/adapters/ChatCompletionsAPIHelperAdapter.js +18 -4
- package/dist/middleware/helpers/adapters/ChatCompletionsAPIHelperAdapter.js.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.d.ts +5 -0
- package/dist/orchestrator/CortexOrchestrator.d.ts.map +1 -1
- package/dist/orchestrator/CortexOrchestrator.js +77 -11
- package/dist/orchestrator/CortexOrchestrator.js.map +1 -1
- package/dist/orchestrator/requirementsVerification.d.ts +11 -0
- package/dist/orchestrator/requirementsVerification.d.ts.map +1 -1
- package/dist/orchestrator/requirementsVerification.js +71 -1
- package/dist/orchestrator/requirementsVerification.js.map +1 -1
- package/dist/tools/ToolProfile.d.ts +7 -0
- package/dist/tools/ToolProfile.d.ts.map +1 -1
- package/dist/tools/ToolProfile.js +13 -0
- package/dist/tools/ToolProfile.js.map +1 -1
- package/dist/tools/registries/BaseToolRegistry.d.ts.map +1 -1
- package/dist/tools/registries/BaseToolRegistry.js +6 -2
- package/dist/tools/registries/BaseToolRegistry.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, resolveLiftNudge, resolveHeadlessDropAskUser, resolveDeferredLoading, isNarrowProfile, isToolAllowedByProfile, applyToolProfile, webToolBlocked } from '../tools/ToolProfile.js';
|
|
33
|
+
import { resolveToolProfile, resolveToolAnchor, resolveFrameProfile, resolveLiftNudge, resolveHeadlessDropAskUser, resolveDeferredLoading, isNarrowProfile, isToolAllowedByProfile, applyToolProfile, webToolBlocked, resolveVisionHelperModel } 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';
|
|
@@ -44,7 +44,7 @@ import { classifyToolOutcome } from '../training/toolOutcome.js';
|
|
|
44
44
|
import { LoopLadder, formatLadderSignal } from '../training/loopLadder.js';
|
|
45
45
|
import { shouldNudgeInaction, formatInactionNudge } from './inactionGuard.js';
|
|
46
46
|
import { applyImageTtlForRequest } from './imageTtl.js';
|
|
47
|
-
import { verifyRequirements, resolveEndTurnRequirementsMode } from './requirementsVerification.js';
|
|
47
|
+
import { verifyRequirements, resolveEndTurnRequirementsMode, resolveEndTurnRequirementsStrict } from './requirementsVerification.js';
|
|
48
48
|
import { verifyIntegrity, resolveEndTurnIntegrityMode } from './integrityVerification.js';
|
|
49
49
|
import { detectSurrenderText, resolveSurrenderNudgeMode, SURRENDER_REMINDER } from './turnEndGuards.js';
|
|
50
50
|
import { ModelRouterMatrix } from '../training/ModelRouterMatrix.js';
|
|
@@ -64,6 +64,8 @@ import { detectTailRepetition, tailLoopGuardEnabled } from './tailRepetitionDete
|
|
|
64
64
|
import { classifyEmptyResponse, emptyResponseNudge } from './emptyResponseClassifier.js';
|
|
65
65
|
// PTC: Progressive tool loading for non-PTC providers
|
|
66
66
|
import { ClientSideToolFilter } from '../tools/ClientSideToolFilter.js';
|
|
67
|
+
/** Grace the orchestrator's outer tool-abort waits past the tool's own deadline (ShellTool promote/kill fires first; 4.90.1). */
|
|
68
|
+
const OUTER_TIMEOUT_GRACE_MS = 30_000;
|
|
67
69
|
/**
|
|
68
70
|
* Cortex Orchestrator - Phase 2.1
|
|
69
71
|
*
|
|
@@ -478,7 +480,7 @@ export class CortexOrchestrator {
|
|
|
478
480
|
const catalogVision = this.getModel(this.currentModelId ?? '')?.vision === true;
|
|
479
481
|
return [...toolFactory.getAllTools(), ...extras]
|
|
480
482
|
.filter((t) => endTurnGateOn || t.name !== 'EndTurn')
|
|
481
|
-
.filter((t) => catalogVision || t.name !== 'ReadImage')
|
|
483
|
+
.filter((t) => catalogVision || !!resolveVisionHelperModel(process.env) || t.name !== 'ReadImage')
|
|
482
484
|
.filter((t) => (seen.has(t.name) ? false : (seen.add(t.name), true)))
|
|
483
485
|
.map((t) => ({
|
|
484
486
|
name: namingHandler.convertName(t.name, convention),
|
|
@@ -634,7 +636,7 @@ export class CortexOrchestrator {
|
|
|
634
636
|
const visionOn = model?.vision === true;
|
|
635
637
|
const factoryTools = this.applyBaseToolAllowlist(toolFactory.getAllTools()
|
|
636
638
|
.filter(t => endTurnGateOn || t.name !== 'EndTurn')
|
|
637
|
-
.filter(t => visionOn || t.name !== 'ReadImage'));
|
|
639
|
+
.filter(t => visionOn || !!resolveVisionHelperModel(process.env) || t.name !== 'ReadImage'));
|
|
638
640
|
// Tool-profile: MCP/management/context tools bypass ToolFactory, so the
|
|
639
641
|
// bash-only arm must suppress them here or the surface leaks (lean keeps
|
|
640
642
|
// them — they are part of the "guided" surface under test).
|
|
@@ -898,7 +900,7 @@ export class CortexOrchestrator {
|
|
|
898
900
|
// Thread the CARD's anchor to the executors (per-orchestrator config, by
|
|
899
901
|
// reference) so framing-aware executor behavior (ShellTool steering
|
|
900
902
|
// default) is model-card/registry-scoped, not process-global env.
|
|
901
|
-
this.executorRegistry.updateConfig({ activeAnchorProfile: this.cardAnchorProfile, allowCommandSubstitution: this.approvalMode.autoApproveActions });
|
|
903
|
+
this.executorRegistry.updateConfig({ activeAnchorProfile: this.cardAnchorProfile, allowCommandSubstitution: this.approvalMode.autoApproveActions, headless: this.approvalMode.autoApproveActions });
|
|
902
904
|
if (toolsToUse && toolsToUse.length > 0) {
|
|
903
905
|
toolsToUse = this.applyAnchorIfArmed(toolsToUse);
|
|
904
906
|
}
|
|
@@ -1909,11 +1911,16 @@ export class CortexOrchestrator {
|
|
|
1909
1911
|
break;
|
|
1910
1912
|
}
|
|
1911
1913
|
// Phase 2.5 Day 3: Execute tools with timeout support
|
|
1914
|
+
// 🔴 4.90.1: this OUTER abort must not race ShellTool's own deadline — ShellTool promotes a
|
|
1915
|
+
// still-running Bash to the background AT TOOL_TIMEOUT_MS (TOOL_TIMEOUT_MODE), and an outer
|
|
1916
|
+
// abort at the same instant made the promote inert (v4 pro/train-fasttext: 22 "cancelled",
|
|
1917
|
+
// 0 promotions). The outer timer is the last-resort cap for hung tools: it fires a grace
|
|
1918
|
+
// period AFTER the tool's own deadline.
|
|
1912
1919
|
const abortController = new AbortController();
|
|
1913
1920
|
const timeoutId = setTimeout(() => {
|
|
1914
|
-
console.warn(`[Orchestrator Phase 2.5] Tool execution timeout after ${TOOL_TIMEOUT_MS}ms`);
|
|
1921
|
+
console.warn(`[Orchestrator Phase 2.5] Tool execution timeout after ${TOOL_TIMEOUT_MS + OUTER_TIMEOUT_GRACE_MS}ms`);
|
|
1915
1922
|
abortController.abort();
|
|
1916
|
-
}, TOOL_TIMEOUT_MS);
|
|
1923
|
+
}, TOOL_TIMEOUT_MS + OUTER_TIMEOUT_GRACE_MS);
|
|
1917
1924
|
// Track which tool_use_ids have been processed to avoid duplicates in error handler
|
|
1918
1925
|
const processedToolUseIds = new Set();
|
|
1919
1926
|
try {
|
|
@@ -2095,6 +2102,8 @@ export class CortexOrchestrator {
|
|
|
2095
2102
|
verification: etUse?.input?.verification,
|
|
2096
2103
|
userTaskText: this.lastRealUserText(),
|
|
2097
2104
|
turnUsedMutatingTool,
|
|
2105
|
+
strict: resolveEndTurnRequirementsStrict(process.env),
|
|
2106
|
+
toolOutputs: thisTurnToolOutputs.join('\n'),
|
|
2098
2107
|
});
|
|
2099
2108
|
if (s4.ok) {
|
|
2100
2109
|
endTurnCalled = true;
|
|
@@ -2156,6 +2165,7 @@ export class CortexOrchestrator {
|
|
|
2156
2165
|
// Item 7: scrub ReadImage payloads from metadata BEFORE persist;
|
|
2157
2166
|
// the images inject as a user message after the results land.
|
|
2158
2167
|
const pendingImages = this.collectPendingImages(toolResults, effectiveModel.vision === true);
|
|
2168
|
+
await this.applyVisionHandoffs();
|
|
2159
2169
|
// Create tool_result messages and add to history
|
|
2160
2170
|
for (const toolResult of toolResults) {
|
|
2161
2171
|
const toolResultMessageId = uuidv4();
|
|
@@ -3196,7 +3206,7 @@ export class CortexOrchestrator {
|
|
|
3196
3206
|
// Thread the CARD's anchor to the executors (per-orchestrator config, by
|
|
3197
3207
|
// reference) so framing-aware executor behavior (ShellTool steering
|
|
3198
3208
|
// default) is model-card/registry-scoped, not process-global env.
|
|
3199
|
-
this.executorRegistry.updateConfig({ activeAnchorProfile: this.cardAnchorProfile, allowCommandSubstitution: this.approvalMode.autoApproveActions });
|
|
3209
|
+
this.executorRegistry.updateConfig({ activeAnchorProfile: this.cardAnchorProfile, allowCommandSubstitution: this.approvalMode.autoApproveActions, headless: this.approvalMode.autoApproveActions });
|
|
3200
3210
|
if (toolsToUse && toolsToUse.length > 0) {
|
|
3201
3211
|
toolsToUse = this.applyAnchorIfArmed(toolsToUse);
|
|
3202
3212
|
}
|
|
@@ -3735,14 +3745,15 @@ export class CortexOrchestrator {
|
|
|
3735
3745
|
if (toolCallIteration >= MAX_TOOL_ITERATIONS) {
|
|
3736
3746
|
break;
|
|
3737
3747
|
}
|
|
3738
|
-
// Execute tools with timeout
|
|
3748
|
+
// Execute tools with timeout (outer cap fires a grace period AFTER the tool's own deadline —
|
|
3749
|
+
// see the non-stream site: ShellTool's promote-at-deadline must win the race; 4.90.1)
|
|
3739
3750
|
const abortController = new AbortController();
|
|
3740
3751
|
const timeoutId = setTimeout(() => {
|
|
3741
3752
|
if (this.config.debug) {
|
|
3742
|
-
console.warn(`[Orchestrator Streaming] Tool execution timeout after ${TOOL_TIMEOUT_MS}ms`);
|
|
3753
|
+
console.warn(`[Orchestrator Streaming] Tool execution timeout after ${TOOL_TIMEOUT_MS + OUTER_TIMEOUT_GRACE_MS}ms`);
|
|
3743
3754
|
}
|
|
3744
3755
|
abortController.abort();
|
|
3745
|
-
}, TOOL_TIMEOUT_MS);
|
|
3756
|
+
}, TOOL_TIMEOUT_MS + OUTER_TIMEOUT_GRACE_MS);
|
|
3746
3757
|
try {
|
|
3747
3758
|
// Execute tools (reuse existing method)
|
|
3748
3759
|
const toolResults = await this.handleToolCalls(toolUseBlocks, abortController.signal, structuredOutputState);
|
|
@@ -3808,6 +3819,7 @@ export class CortexOrchestrator {
|
|
|
3808
3819
|
}
|
|
3809
3820
|
// Item 7: scrub ReadImage payloads before persist (streaming parity).
|
|
3810
3821
|
const pendingImages = this.collectPendingImages(toolResults, effectiveModel.vision === true);
|
|
3822
|
+
await this.applyVisionHandoffs();
|
|
3811
3823
|
// Create tool_result messages and save to history
|
|
3812
3824
|
for (const toolResult of toolResults) {
|
|
3813
3825
|
// Emit tool result as streaming chunk for CLI display
|
|
@@ -6801,6 +6813,43 @@ export class CortexOrchestrator {
|
|
|
6801
6813
|
* hallucinated/PTC call that slipped the surface gate), the result is
|
|
6802
6814
|
* rewritten into an actionable error — a text-only model must never have
|
|
6803
6815
|
* an image dangled at it. */
|
|
6816
|
+
/** Vision hand-off queue (filled by collectPendingImages for text-only primaries). */
|
|
6817
|
+
pendingVisionHandoffs = [];
|
|
6818
|
+
/** Resolve queued ReadImage hand-offs through the helper middleware: the vision helper's TEXT
|
|
6819
|
+
* replaces the tool_result content. Failures degrade to the pre-hand-off guidance (bash OCR). */
|
|
6820
|
+
async applyVisionHandoffs() {
|
|
6821
|
+
const queue = this.pendingVisionHandoffs;
|
|
6822
|
+
this.pendingVisionHandoffs = [];
|
|
6823
|
+
for (const h of queue) {
|
|
6824
|
+
const modelId = resolveVisionHelperModel(process.env) || 'deepseek-v4-flash-vision-exp';
|
|
6825
|
+
try {
|
|
6826
|
+
const t0 = Date.now();
|
|
6827
|
+
const text = await this.helperMiddleware.describeImage({
|
|
6828
|
+
mediaType: h.payload.mediaType, base64: h.payload.base64, prompt: h.prompt, filePath: h.filePath, helperModelId: modelId,
|
|
6829
|
+
});
|
|
6830
|
+
h.tr.is_error = false;
|
|
6831
|
+
h.tr.content =
|
|
6832
|
+
`[ReadImage → vision helper ${modelId}${h.filePath ? ` on ${h.filePath}` : ''}]\n` +
|
|
6833
|
+
(text || '(the vision helper returned no description)') +
|
|
6834
|
+
`\n\n(Read by the vision helper on your behalf — the active model is text-only. Ask again with a more specific \`prompt\` if you need a different reading.)`;
|
|
6835
|
+
if (this.config.debug)
|
|
6836
|
+
console.log(`[VisionHandoff] ${modelId} described ${h.filePath || 'image'} in ${Date.now() - t0}ms (${(text || '').length} chars)`);
|
|
6837
|
+
const store = this.getDecisionStore();
|
|
6838
|
+
if (store) {
|
|
6839
|
+
void store.recordEvent({
|
|
6840
|
+
sessionId: this.currentSessionId ?? 'unknown', kind: 'steering_injected', toolName: 'ReadImage',
|
|
6841
|
+
detail: { kind: 'vision_handoff', helper: modelId, chars: (text || '').length, filePath: h.filePath },
|
|
6842
|
+
}).catch(() => { });
|
|
6843
|
+
}
|
|
6844
|
+
}
|
|
6845
|
+
catch (err) {
|
|
6846
|
+
h.tr.is_error = true;
|
|
6847
|
+
h.tr.content =
|
|
6848
|
+
`ReadImage vision hand-off failed (${modelId}: ${err?.message || err}). ` +
|
|
6849
|
+
'Extract what you need through bash instead (e.g. tesseract OCR, ffprobe, PIL).';
|
|
6850
|
+
}
|
|
6851
|
+
}
|
|
6852
|
+
}
|
|
6804
6853
|
collectPendingImages(toolResults, modelVision) {
|
|
6805
6854
|
const out = [];
|
|
6806
6855
|
for (const tr of toolResults) {
|
|
@@ -6811,6 +6860,16 @@ export class CortexOrchestrator {
|
|
|
6811
6860
|
if (tr.is_error)
|
|
6812
6861
|
continue;
|
|
6813
6862
|
if (!modelVision) {
|
|
6863
|
+
// VISION HAND-OFF (2026-09-02): a text-only primary called ReadImage. If a vision helper
|
|
6864
|
+
// is configured, queue the payload for the helper middleware (applyVisionHandoffs runs
|
|
6865
|
+
// right after this at both call sites and REPLACES the tool_result with the helper's
|
|
6866
|
+
// text). The primary never receives an image payload — item 7's safety intent holds.
|
|
6867
|
+
if (resolveVisionHelperModel(process.env)) {
|
|
6868
|
+
this.pendingVisionHandoffs.push({ tr, payload, prompt: tr.metadata?.imagePrompt, filePath: tr.metadata?.imageFilePath });
|
|
6869
|
+
delete tr.metadata.imagePrompt;
|
|
6870
|
+
delete tr.metadata.imageFilePath;
|
|
6871
|
+
continue;
|
|
6872
|
+
}
|
|
6814
6873
|
tr.is_error = true;
|
|
6815
6874
|
tr.content =
|
|
6816
6875
|
'ReadImage requires a vision-capable model — the active model cannot see images. ' +
|
|
@@ -7055,6 +7114,13 @@ export class CortexOrchestrator {
|
|
|
7055
7114
|
const store = this.getDecisionStore();
|
|
7056
7115
|
if (!store)
|
|
7057
7116
|
return result;
|
|
7117
|
+
// TOOL_TIMEOUT_MODE engagement evidence: a promoted-to-background tool call.
|
|
7118
|
+
if (result.metadata?.promotedToBackground) {
|
|
7119
|
+
void store.recordEvent({
|
|
7120
|
+
sessionId: this.currentSessionId ?? 'unknown', kind: 'steering_injected', toolName: toolUse.name,
|
|
7121
|
+
detail: { kind: 'tool_promoted', bashId: result.metadata.promotedToBackground },
|
|
7122
|
+
}).catch(() => { });
|
|
7123
|
+
}
|
|
7058
7124
|
// Unified outcome layer (docs/UNIFIED_OUTCOME_LADDER.md): ONE failure
|
|
7059
7125
|
// semantics for recording + reminders. Critically, a bash command that
|
|
7060
7126
|
// ran and exited nonzero is a FAILURE here even though its tool result
|