feat(context): wire headroom proxy and injection budget shadow paths

Add fail-open headroom provider routing for goosed sync, context-budget
planning with shadow telemetry events, and fix portal-resume smoke to skip
force_deep_reasoning by default so session round-trip checks stay fast.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-09-09 21:24:02 +08:00
parent d691fed8a6
commit 49a1a68a29
11 changed files with 849 additions and 9 deletions
+28
View File
@@ -52,6 +52,8 @@ import {
} from './chat-task-intent-config.mjs';
import { extractAgentRunExperience } from './experience-extractor.mjs';
import { executeRainPipeline, isRainModeMessage } from './rain-service/index.mjs';
import { buildContextBudgetResolvedEvent, resolveContextBudgetMode } from './context-budget.mjs';
import { buildHeadroomRunObservation, resolveHeadroomMode } from './memind-headroom-policy.mjs';
const DEFAULT_RUN_RETRY_DELAYS_MS = [1500, 5000, 15000];
const TERMINAL_STATUSES = new Set(['succeeded', 'failed']);
@@ -1946,6 +1948,11 @@ export function createAgentRunGateway({
sessionId: row.agent_session_id ?? null,
});
await appendEvent(runId, 'intent_routed', routing);
if (resolveHeadroomMode() !== 'off') {
await appendEvent(runId, 'headroom_context_observed', buildHeadroomRunObservation({
skillId: routing?.suggestedSkill ?? routing?.suggested_skill ?? null,
}));
}
if (routingDecision === CHAT_INTENT_ROUTE.AGENT && chatIntentRouter?.applyAgentOrchestration) {
const grantedSkills = await resolveGrantedSkills(row.user_id);
let goalContext = null;
@@ -1997,11 +2004,32 @@ export function createAgentRunGateway({
}
}
if (!rainHandoff) {
let preparedContext = null;
if (
resolveContextBudgetMode() !== 'off' &&
chatIntentRouter?.prepareAgentOrchestrationContext
) {
preparedContext = chatIntentRouter.prepareAgentOrchestrationContext(
userMessage,
routing,
{
grantedSkills,
memoryContext: agentMemoryContext,
goalContext,
runtimeContext,
},
);
const budgetEvent = buildContextBudgetResolvedEvent(preparedContext.budgetPlan);
if (budgetEvent) {
await appendEvent(runId, 'context_budget_resolved', budgetEvent);
}
}
userMessage = chatIntentRouter.applyAgentOrchestration(userMessage, routing, {
grantedSkills,
memoryContext: agentMemoryContext,
goalContext,
runtimeContext,
preparedContext,
});
}
}
+45 -6
View File
@@ -9,6 +9,7 @@ import {
isProductCampaignIntent,
PAGE_DATA_COLLECT_SKILL_NAME,
} from './chat-skills.mjs';
import { applyContextBudgetToOrchestration } from './context-budget.mjs';
import { isDirectChatSessionId } from './direct-chat-service.mjs';
import {
memoryLimitForIntervention,
@@ -1175,20 +1176,56 @@ export function buildAgentOrchestrationAgentText({
return lines.join('\n');
}
export function applyAgentOrchestrationToUserMessage(
export function prepareAgentOrchestrationContext(
userMessage,
classification,
{ grantedSkills = [], memoryContext = null, goalContext = null, runtimeContext = null } = {},
) {
const displayText = messageDisplayText(userMessage);
const skillPrompt = resolveSkillPrompt(classification?.suggestedSkill, grantedSkills, displayText);
const agentText = buildAgentOrchestrationAgentText({
displayText,
classification,
skillPrompt,
const budgeted = applyContextBudgetToOrchestration({
memoryContext,
goalContext,
runtimeContext,
skillPrompt,
userTask: displayText,
});
return {
displayText,
classification,
skillPrompt: budgeted.skillPrompt,
memoryContext: budgeted.memoryContext,
goalContext: budgeted.goalContext,
runtimeContext: budgeted.runtimeContext,
budgetPlan: budgeted.plan,
budgetApplied: budgeted.applied,
};
}
export function applyAgentOrchestrationToUserMessage(
userMessage,
classification,
{
grantedSkills = [],
memoryContext = null,
goalContext = null,
runtimeContext = null,
preparedContext = null,
} = {},
) {
const prepared = preparedContext ?? prepareAgentOrchestrationContext(userMessage, classification, {
grantedSkills,
memoryContext,
goalContext,
runtimeContext,
});
const agentText = buildAgentOrchestrationAgentText({
displayText: prepared.displayText,
classification: prepared.classification,
skillPrompt: prepared.skillPrompt,
memoryContext: prepared.memoryContext,
goalContext: prepared.goalContext,
runtimeContext: prepared.runtimeContext,
});
const content = Array.isArray(userMessage?.content)
? userMessage.content.map((item, index) => {
@@ -1200,7 +1237,7 @@ export function applyAgentOrchestrationToUserMessage(
: [{ type: 'text', text: agentText }];
const metadata = {
...(userMessage?.metadata ?? {}),
displayText: displayText || undefined,
displayText: prepared.displayText || undefined,
chatIntentRoute: CHAT_INTENT_ROUTE.AGENT,
chatIntentSource: classification?.source ?? null,
userVisible: userMessage?.metadata?.userVisible ?? true,
@@ -1901,6 +1938,7 @@ export function createChatIntentRouter(options = {}) {
classifySessionAction,
resolveAgentMemoryContext,
applyAgentOrchestration: applyAgentOrchestrationToUserMessage,
prepareAgentOrchestrationContext,
};
}
@@ -2005,5 +2043,6 @@ export function createManagedChatIntentRouter({
},
applyAgentOrchestration: applyAgentOrchestrationToUserMessage,
prepareAgentOrchestrationContext,
};
}
+360
View File
@@ -0,0 +1,360 @@
import crypto from 'node:crypto';
export const CONTEXT_BUDGET_MODES = Object.freeze(['off', 'shadow', 'active']);
export const DEFAULT_CONTEXT_BUDGET_MAX_CHARS = 12_000;
const SOURCE_PRIORITY = Object.freeze({
user_task: 100,
skill_prompt: 90,
goal: 85,
temporal: 75,
memory: 65,
harness: 50,
});
const HARNESS_TITLE_PRIORITY = Object.freeze({
'TKMind 用户空间沙箱': 95,
'TKMind 当前时间基准': 94,
'TKMind 代码委托策略': 88,
'TKMind 日程写入规则': 82,
'TKMind 定时自动任务规则': 82,
'TKMind 长期记忆规则': 58,
'TKMind 用户偏好画像': 57,
'TKMind 已沉淀用户记忆': 56,
});
function normalizeMode(value, fallback = 'off') {
const raw = String(value ?? fallback).trim().toLowerCase();
return CONTEXT_BUDGET_MODES.includes(raw) ? raw : fallback;
}
export function resolveContextBudgetMode(env = process.env) {
return normalizeMode(env.MEMIND_CONTEXT_BUDGET_MODE, 'off');
}
export function resolveContextBudgetMaxChars(env = process.env) {
const raw = Number(env.MEMIND_CONTEXT_BUDGET_MAX_CHARS ?? DEFAULT_CONTEXT_BUDGET_MAX_CHARS);
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : DEFAULT_CONTEXT_BUDGET_MAX_CHARS;
}
export function fingerprintContent(text) {
const normalized = String(text ?? '')
.replace(/\s+/g, ' ')
.trim()
.toLowerCase();
if (!normalized) return null;
return crypto.createHash('sha256').update(normalized).digest('hex').slice(0, 16);
}
function blockPriority(block) {
const source = String(block?.source ?? '').trim();
const base = SOURCE_PRIORITY[source] ?? 40;
if (source !== 'harness') return base;
const title = String(block?.title ?? block?.label ?? '').trim();
return HARNESS_TITLE_PRIORITY[title] ?? base;
}
function blockChars(block) {
return String(block?.text ?? block?.content ?? '').length;
}
function normalizeMemoryBlock(item) {
const label = String(item?.label ?? item?.title ?? '').trim();
const text = String(item?.text ?? item?.content ?? item?.summary ?? '').trim();
if (!text) return null;
return {
source: 'memory',
id: String(item?.id ?? item?.memoryId ?? label ?? text.slice(0, 32)),
label: label || null,
text,
fingerprint: fingerprintContent(text),
priority: SOURCE_PRIORITY.memory,
chars: text.length,
};
}
function normalizeHarnessBlock(entry) {
const title = String(entry?.title ?? '').trim();
const text = String(entry?.content ?? entry?.text ?? '').trim();
if (!text) return null;
return {
source: 'harness',
id: title || text.slice(0, 32),
title: title || null,
text,
fingerprint: fingerprintContent(text),
priority: blockPriority({ source: 'harness', title }),
chars: text.length,
};
}
export function buildContextBudgetBlocks({
memories = [],
temporalText = '',
goalEnvelope = '',
harnessEntries = [],
skillPrompt = '',
userTask = '',
} = {}) {
const blocks = [];
const task = String(userTask ?? '').trim();
if (task) {
blocks.push({
source: 'user_task',
id: 'user_task',
text: task,
fingerprint: fingerprintContent(task),
priority: SOURCE_PRIORITY.user_task,
chars: task.length,
});
}
const skill = String(skillPrompt ?? '').trim();
if (skill) {
blocks.push({
source: 'skill_prompt',
id: 'skill_prompt',
text: skill,
fingerprint: fingerprintContent(skill),
priority: SOURCE_PRIORITY.skill_prompt,
chars: skill.length,
});
}
const goal = String(goalEnvelope ?? '').trim();
if (goal) {
blocks.push({
source: 'goal',
id: 'goal',
text: goal,
fingerprint: fingerprintContent(goal),
priority: SOURCE_PRIORITY.goal,
chars: goal.length,
});
}
const temporal = String(temporalText ?? '').trim();
if (temporal) {
blocks.push({
source: 'temporal',
id: 'temporal',
text: temporal,
fingerprint: fingerprintContent(temporal),
priority: SOURCE_PRIORITY.temporal,
chars: temporal.length,
});
}
for (const item of Array.isArray(memories) ? memories : []) {
const block = normalizeMemoryBlock(item);
if (block) blocks.push(block);
}
for (const entry of Array.isArray(harnessEntries) ? harnessEntries : []) {
const block = normalizeHarnessBlock(entry);
if (block) blocks.push(block);
}
return blocks;
}
export function planContextBudget(input = {}, { maxChars, env = process.env } = {}) {
const mode = resolveContextBudgetMode(env);
const budget = Number.isFinite(maxChars) ? maxChars : resolveContextBudgetMaxChars(env);
const blocks = buildContextBudgetBlocks(input);
const seenFingerprints = new Set();
const kept = [];
const droppedItems = [];
const duplicateItems = [];
const sorted = [...blocks].sort((a, b) => {
const priorityDelta = blockPriority(b) - blockPriority(a);
if (priorityDelta !== 0) return priorityDelta;
return blockChars(a) - blockChars(b);
});
let totalChars = 0;
for (const block of sorted) {
if (block.source === 'user_task') {
kept.push(block);
totalChars += block.chars;
if (block.fingerprint) seenFingerprints.add(block.fingerprint);
continue;
}
if (block.fingerprint && seenFingerprints.has(block.fingerprint)) {
duplicateItems.push({
source: block.source,
id: block.id,
reason: 'duplicate_fingerprint',
fingerprint: block.fingerprint,
chars: block.chars,
});
continue;
}
if (totalChars + block.chars > budget) {
droppedItems.push({
source: block.source,
id: block.id,
reason: 'over_budget',
fingerprint: block.fingerprint ?? null,
chars: block.chars,
priority: blockPriority(block),
});
continue;
}
kept.push(block);
totalChars += block.chars;
if (block.fingerprint) seenFingerprints.add(block.fingerprint);
}
const fingerprints = [...seenFingerprints];
return {
mode,
budget,
blocks: kept,
droppedItems,
duplicateItems,
totalChars,
fingerprints,
inputBlockCount: blocks.length,
keptBlockCount: kept.length,
};
}
export function applyContextBudgetToOrchestration({
memoryContext = null,
goalContext = null,
runtimeContext = null,
skillPrompt = '',
userTask = '',
plan = null,
env = process.env,
} = {}) {
const mode = resolveContextBudgetMode(env);
if (mode === 'off') {
return {
memoryContext,
goalContext,
runtimeContext,
skillPrompt,
plan: null,
applied: false,
};
}
const resolvedPlan = plan ?? planContextBudget({
memories: memoryContext?.injectionEnabled ? memoryContext.memories : [],
temporalText: runtimeContext?.injectionEnabled ? runtimeContext.injectionText : '',
goalEnvelope: goalContext?.injectionEnabled ? goalContext.envelope : '',
skillPrompt,
userTask,
}, { env });
if (mode === 'shadow') {
return {
memoryContext,
goalContext,
runtimeContext,
skillPrompt,
plan: resolvedPlan,
applied: false,
};
}
const keptBySource = {
memory: new Set(),
goal: false,
temporal: false,
skill_prompt: false,
};
for (const block of resolvedPlan.blocks) {
if (block.source === 'memory') keptBySource.memory.add(block.id);
if (block.source === 'goal') keptBySource.goal = true;
if (block.source === 'temporal') keptBySource.temporal = true;
if (block.source === 'skill_prompt') keptBySource.skill_prompt = true;
}
const nextMemoryContext = memoryContext?.injectionEnabled
? {
...memoryContext,
memories: (Array.isArray(memoryContext.memories) ? memoryContext.memories : [])
.filter((item, index) => {
const label = String(item?.label ?? item?.title ?? '').trim();
const text = String(item?.text ?? item?.content ?? item?.summary ?? '').trim();
const id = String(item?.id ?? item?.memoryId ?? label ?? (text.slice(0, 32) || `memory-${index}`));
return keptBySource.memory.has(id);
}),
}
: memoryContext;
const nextGoalContext = goalContext?.injectionEnabled && !keptBySource.goal
? { ...goalContext, injectionEnabled: false, envelope: '' }
: goalContext;
const nextRuntimeContext = runtimeContext?.injectionEnabled && !keptBySource.temporal
? { ...runtimeContext, injectionEnabled: false, injectionText: '' }
: runtimeContext;
const nextSkillPrompt = keptBySource.skill_prompt ? skillPrompt : '';
return {
memoryContext: nextMemoryContext,
goalContext: nextGoalContext,
runtimeContext: nextRuntimeContext,
skillPrompt: nextSkillPrompt,
plan: resolvedPlan,
applied: true,
};
}
export function applyContextBudgetToHarnessEntries(entries = [], { plan = null, env = process.env } = {}) {
const mode = resolveContextBudgetMode(env);
if (mode !== 'active') {
return {
entries,
plan: plan ?? (mode === 'shadow'
? planContextBudget({ harnessEntries: entries }, { env })
: null),
applied: false,
};
}
const resolvedPlan = plan ?? planContextBudget({ harnessEntries: entries }, { env });
const keptIds = new Set(
resolvedPlan.blocks
.filter((block) => block.source === 'harness')
.map((block) => block.id),
);
return {
entries: entries.filter((entry) => {
const title = String(entry?.title ?? '').trim();
const text = String(entry?.content ?? entry?.text ?? '').trim();
const id = title || text.slice(0, 32);
return keptIds.has(id);
}),
plan: resolvedPlan,
applied: true,
};
}
export function buildContextBudgetResolvedEvent(plan) {
if (!plan) return null;
return {
mode: plan.mode,
budget: plan.budget,
totalChars: plan.totalChars,
inputBlockCount: plan.inputBlockCount,
keptBlockCount: plan.keptBlockCount,
droppedCount: plan.droppedItems.length,
duplicateCount: plan.duplicateItems.length,
droppedItems: plan.droppedItems,
duplicateItems: plan.duplicateItems,
fingerprints: plan.fingerprints,
};
}
+98
View File
@@ -0,0 +1,98 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
applyContextBudgetToHarnessEntries,
applyContextBudgetToOrchestration,
buildContextBudgetBlocks,
fingerprintContent,
planContextBudget,
resolveContextBudgetMode,
} from './context-budget.mjs';
test('resolveContextBudgetMode defaults to off', () => {
assert.equal(resolveContextBudgetMode({}), 'off');
assert.equal(resolveContextBudgetMode({ MEMIND_CONTEXT_BUDGET_MODE: 'shadow' }), 'shadow');
assert.equal(resolveContextBudgetMode({ MEMIND_CONTEXT_BUDGET_MODE: 'bogus' }), 'off');
});
test('fingerprintContent normalizes whitespace and case', () => {
assert.equal(
fingerprintContent(' Hello World '),
fingerprintContent('hello world'),
);
});
test('planContextBudget dedupes duplicate fingerprints across sources', () => {
const duplicate = '用户偏好:喜欢简洁回答';
const plan = planContextBudget({
memories: [{ label: '偏好', text: duplicate }],
harnessEntries: [{ title: 'TKMind 用户偏好画像', content: duplicate }],
userTask: '帮我总结今天的工作',
}, {
maxChars: 10_000,
env: { MEMIND_CONTEXT_BUDGET_MODE: 'shadow' },
});
assert.equal(plan.duplicateItems.length, 1);
assert.equal(plan.keptBlockCount, plan.inputBlockCount - 1);
});
test('planContextBudget drops lower-priority blocks when over budget', () => {
const plan = planContextBudget({
userTask: 'task',
skillPrompt: 'skill',
goalEnvelope: 'goal',
temporalText: 'temporal',
memories: [{ label: 'm1', text: 'memory-one' }],
harnessEntries: [{ title: 'TKMind 已沉淀用户记忆', content: 'stored-memory' }],
}, {
maxChars: 20,
env: { MEMIND_CONTEXT_BUDGET_MODE: 'active' },
});
assert.ok(plan.droppedItems.length > 0);
assert.ok(plan.blocks.some((block) => block.source === 'user_task'));
assert.ok(plan.droppedItems.every((item) => item.source !== 'user_task'));
});
test('applyContextBudgetToOrchestration is no-op in shadow mode', () => {
const memoryContext = {
injectionEnabled: true,
memories: [{ label: 'a', text: 'alpha' }, { label: 'b', text: 'beta' }],
};
const applied = applyContextBudgetToOrchestration({
memoryContext,
userTask: 'do work',
env: { MEMIND_CONTEXT_BUDGET_MODE: 'shadow', MEMIND_CONTEXT_BUDGET_MAX_CHARS: '10' },
});
assert.equal(applied.applied, false);
assert.equal(applied.memoryContext.memories.length, 2);
assert.ok(applied.plan);
});
test('applyContextBudgetToHarnessEntries filters in active mode', () => {
const entries = [
{ title: 'TKMind 当前时间基准', content: 'time anchor' },
{ title: 'TKMind 已沉淀用户记忆', content: 'long '.repeat(200) },
];
const applied = applyContextBudgetToHarnessEntries(entries, {
env: {
MEMIND_CONTEXT_BUDGET_MODE: 'active',
MEMIND_CONTEXT_BUDGET_MAX_CHARS: '40',
},
});
assert.equal(applied.applied, true);
assert.ok(applied.entries.length < entries.length);
assert.ok(applied.entries.some((entry) => entry.title === 'TKMind 当前时间基准'));
});
test('buildContextBudgetBlocks preserves source metadata', () => {
const blocks = buildContextBudgetBlocks({
userTask: 'hello',
harnessEntries: [{ title: 'TKMind 用户空间沙箱', content: 'sandbox rules' }],
});
assert.ok(blocks.some((block) => block.source === 'user_task'));
assert.ok(blocks.some((block) => block.source === 'harness' && block.priority >= 90));
});
+10
View File
@@ -23,3 +23,13 @@ MEMORY_CANDIDATE_ENABLED=0
# Goose v1.49 local session DB (isolated from production memind_sessions)
# GOOSE_SESSION_DB_URL=postgresql://john@127.0.0.1:5432/goose_sessions_v149_dev
# Headroom context compression (Phase 1, local loopback only)
# MEMIND_HEADROOM_MODE=off
# MEMIND_HEADROOM_PROXY_BASE_URL=http://127.0.0.1:8787/v1
# MEMIND_HEADROOM_UPSTREAM_BASE_URL=http://127.0.0.1:18036/v1
# HEADROOM_OUTPUT_SHAPER=0
# Context injection budget (Phase 2, default off — does not open memory injection)
# MEMIND_CONTEXT_BUDGET_MODE=off
# MEMIND_CONTEXT_BUDGET_MAX_CHARS=12000
+26 -1
View File
@@ -20,6 +20,11 @@ import {
buildCursorExecutorLaunchPlan,
cursorExecutorEnabled,
} from './cursor-agent-launch.mjs';
import {
probeHeadroomProxyReachable,
resolveGoosedApiUrlWithHeadroom,
resolveHeadroomMode,
} from './memind-headroom-policy.mjs';
export const MEMIND_DEEPSEEK_NO_THINK_PROVIDER_ID = 'custom_memind_deepseek_no_think';
@@ -885,13 +890,33 @@ async function syncDeepseekNoThinkProfileToGoosed(apiTarget, apiSecret, profile,
...(Array.isArray(profile.models) ? profile.models : []),
].filter(Boolean).map((item) => String(item).trim()).filter(Boolean)),
];
const compatProxyBaseUrl = resolveDeepseekNoThinkProxyBaseUrl();
const headroomMode = resolveHeadroomMode();
const headroomReachable = headroomMode === 'off'
? false
: await probeHeadroomProxyReachable({ fetchImpl });
const headroomRouting = resolveGoosedApiUrlWithHeadroom({
apiUrl: compatProxyBaseUrl,
mode: headroomMode,
headroomReachable,
eligible: true,
});
if (headroomMode !== 'off') {
console.log('[llm-providers] headroom provider routing', {
mode: headroomRouting.mode,
routed: headroomRouting.routed,
fallback: headroomRouting.fallback ?? null,
wouldRouteTo: headroomRouting.wouldRouteTo ?? null,
apiUrl: headroomRouting.apiUrl,
});
}
const goosedProviderId = await upsertCustomProviderOnGoosed(
apiTarget,
apiSecret,
{
name: 'memind_deepseek_no_think',
goosedProviderId: MEMIND_DEEPSEEK_NO_THINK_PROVIDER_ID,
apiUrl: resolveDeepseekNoThinkProxyBaseUrl(),
apiUrl: headroomRouting.apiUrl,
apiKey: profile.apiKey,
models,
defaultModel: profile.defaultModel,
+146
View File
@@ -0,0 +1,146 @@
import {
resolveDeepseekNoThinkProxyBaseUrl,
} from './deepseek-no-think-proxy.mjs';
export const DEFAULT_HEADROOM_PROXY_PORT = 8787;
export const MEMIND_HEADROOM_MODES = Object.freeze(['off', 'shadow', 'active']);
export const DEFAULT_HEADROOM_EXCLUDED_SKILLS = Object.freeze([
'static-page-publish',
'page-data-collect',
'aider-development',
]);
function normalizeMode(value, fallback = 'off') {
const raw = String(value ?? fallback).trim().toLowerCase();
return MEMIND_HEADROOM_MODES.includes(raw) ? raw : fallback;
}
export function resolveHeadroomMode(env = process.env) {
return normalizeMode(env.MEMIND_HEADROOM_MODE, 'off');
}
export function resolveHeadroomProxyPort(env = process.env) {
const port = Number(env.MEMIND_HEADROOM_PROXY_PORT ?? DEFAULT_HEADROOM_PROXY_PORT);
return Number.isFinite(port) && port > 0 ? port : DEFAULT_HEADROOM_PROXY_PORT;
}
export function resolveHeadroomProxyBaseUrl(env = process.env) {
const explicit = String(env.MEMIND_HEADROOM_PROXY_BASE_URL ?? '').trim();
if (explicit) return explicit.replace(/\/$/, '');
const host = String(env.MEMIND_HEADROOM_PROXY_HOST ?? '127.0.0.1').trim() || '127.0.0.1';
return `http://${host}:${resolveHeadroomProxyPort(env)}/v1`;
}
export function resolveHeadroomUpstreamBaseUrl(env = process.env) {
const explicit = String(env.MEMIND_HEADROOM_UPSTREAM_BASE_URL ?? '').trim();
if (explicit) return explicit.replace(/\/$/, '');
return resolveDeepseekNoThinkProxyBaseUrl(env);
}
export function parseHeadroomExcludedSkills(env = process.env) {
const raw = String(env.MEMIND_HEADROOM_EXCLUDED_SKILLS ?? '').trim();
if (!raw) return [...DEFAULT_HEADROOM_EXCLUDED_SKILLS];
return raw.split(',').map((item) => item.trim()).filter(Boolean);
}
export function isHeadroomEligibleForSkill(skillId, env = process.env) {
if (resolveHeadroomMode(env) === 'off') return false;
const skill = String(skillId ?? '').trim();
if (!skill) return true;
return !parseHeadroomExcludedSkills(env).includes(skill);
}
/**
* Decide whether goosed should talk to headroom instead of the direct compat proxy.
* fail-open: active mode falls back to the original apiUrl when headroom is unreachable.
*/
export function resolveGoosedApiUrlWithHeadroom({
apiUrl,
mode = resolveHeadroomMode(),
headroomReachable = false,
eligible = true,
env = process.env,
} = {}) {
const normalizedMode = normalizeMode(mode, 'off');
const original = String(apiUrl ?? '').trim();
if (!original || normalizedMode === 'off' || !eligible) {
return {
apiUrl: original,
mode: normalizedMode,
routed: false,
eligible,
};
}
const headroomBase = resolveHeadroomProxyBaseUrl(env);
if (normalizedMode === 'shadow') {
return {
apiUrl: original,
mode: 'shadow',
routed: false,
eligible: true,
wouldRouteTo: headroomBase,
upstreamBaseUrl: resolveHeadroomUpstreamBaseUrl(env),
};
}
if (!headroomReachable) {
return {
apiUrl: original,
mode: 'active',
routed: false,
eligible: true,
fallback: 'headroom_unreachable',
wouldRouteTo: headroomBase,
};
}
return {
apiUrl: headroomBase,
mode: 'active',
routed: true,
eligible: true,
upstreamBaseUrl: resolveHeadroomUpstreamBaseUrl(env),
};
}
export function buildHeadroomRunObservation({ skillId, env = process.env } = {}) {
const mode = resolveHeadroomMode(env);
const eligible = isHeadroomEligibleForSkill(skillId, env);
return {
mode,
eligible,
excludedSkills: parseHeadroomExcludedSkills(env),
proxyBaseUrl: mode !== 'off' ? resolveHeadroomProxyBaseUrl(env) : null,
upstreamBaseUrl: mode !== 'off' ? resolveHeadroomUpstreamBaseUrl(env) : null,
};
}
export async function probeHeadroomProxyReachable({
baseUrl,
fetchImpl = fetch,
timeoutMs = 1500,
env = process.env,
} = {}) {
const root = String(baseUrl ?? resolveHeadroomProxyBaseUrl(env))
.trim()
.replace(/\/v1\/?$/i, '');
if (!root) return false;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetchImpl(`${root}/v1/models`, {
method: 'GET',
signal: controller.signal,
headers: { Authorization: 'Bearer headroom-probe' },
});
// Proxy alive when it responds, even with auth/upstream errors.
return response.status > 0;
} catch {
return false;
} finally {
clearTimeout(timer);
}
}
+79
View File
@@ -0,0 +1,79 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
buildHeadroomRunObservation,
isHeadroomEligibleForSkill,
resolveGoosedApiUrlWithHeadroom,
resolveHeadroomMode,
resolveHeadroomProxyBaseUrl,
} from './memind-headroom-policy.mjs';
test('resolveHeadroomMode defaults to off and rejects unknown values', () => {
assert.equal(resolveHeadroomMode({}), 'off');
assert.equal(resolveHeadroomMode({ MEMIND_HEADROOM_MODE: 'shadow' }), 'shadow');
assert.equal(resolveHeadroomMode({ MEMIND_HEADROOM_MODE: 'bogus' }), 'off');
});
test('resolveHeadroomProxyBaseUrl honors explicit override', () => {
assert.equal(
resolveHeadroomProxyBaseUrl({ MEMIND_HEADROOM_PROXY_BASE_URL: 'http://127.0.0.1:9999/v1' }),
'http://127.0.0.1:9999/v1',
);
});
test('isHeadroomEligibleForSkill excludes page and code skills by default', () => {
assert.equal(isHeadroomEligibleForSkill('web', { MEMIND_HEADROOM_MODE: 'shadow' }), true);
assert.equal(
isHeadroomEligibleForSkill('static-page-publish', { MEMIND_HEADROOM_MODE: 'shadow' }),
false,
);
assert.equal(
isHeadroomEligibleForSkill('page-data-collect', { MEMIND_HEADROOM_MODE: 'active' }),
false,
);
});
test('resolveGoosedApiUrlWithHeadroom is fail-open in active mode', () => {
const original = 'http://127.0.0.1:18036/v1';
const shadow = resolveGoosedApiUrlWithHeadroom({
apiUrl: original,
mode: 'shadow',
headroomReachable: false,
eligible: true,
env: { MEMIND_HEADROOM_PROXY_BASE_URL: 'http://127.0.0.1:8787/v1' },
});
assert.equal(shadow.apiUrl, original);
assert.equal(shadow.routed, false);
assert.equal(shadow.wouldRouteTo, 'http://127.0.0.1:8787/v1');
const activeFallback = resolveGoosedApiUrlWithHeadroom({
apiUrl: original,
mode: 'active',
headroomReachable: false,
eligible: true,
env: { MEMIND_HEADROOM_PROXY_BASE_URL: 'http://127.0.0.1:8787/v1' },
});
assert.equal(activeFallback.apiUrl, original);
assert.equal(activeFallback.fallback, 'headroom_unreachable');
const active = resolveGoosedApiUrlWithHeadroom({
apiUrl: original,
mode: 'active',
headroomReachable: true,
eligible: true,
env: { MEMIND_HEADROOM_PROXY_BASE_URL: 'http://127.0.0.1:8787/v1' },
});
assert.equal(active.apiUrl, 'http://127.0.0.1:8787/v1');
assert.equal(active.routed, true);
});
test('buildHeadroomRunObservation reports skill eligibility', () => {
const observation = buildHeadroomRunObservation({
skillId: 'static-page-publish',
env: { MEMIND_HEADROOM_MODE: 'shadow' },
});
assert.equal(observation.mode, 'shadow');
assert.equal(observation.eligible, false);
assert.equal(observation.proxyBaseUrl, 'http://127.0.0.1:8787/v1');
});
+7 -1
View File
@@ -7,6 +7,7 @@ import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { prepareGooseV149CheckEnv } from './goose-v149-canary.mjs';
import { waitForAgentRunWorkerIdle } from './goose-v149-worker-idle.mjs';
import {
createReporter,
loginViaApi,
@@ -47,7 +48,8 @@ async function createGoosedAgentRun(baseUrl, cookie, { sessionId, message }) {
body: JSON.stringify({
request_id: requestId,
session_id: sessionId,
force_deep_reasoning: process.env.GOOSE_V149_PORTAL_RESUME_FORCE_DEEP !== '0',
// Resume smoke validates session round-trip, not deep-reasoning latency.
force_deep_reasoning: process.env.GOOSE_V149_PORTAL_RESUME_FORCE_DEEP === '1',
user_message: {
id: crypto.randomUUID(),
role: 'user',
@@ -104,6 +106,10 @@ async function main() {
return;
}
await waitForAgentRunWorkerIdle(root, process.env, {
logPrefix: '[goose-v149-portal-resume]',
});
const reporter = createReporter();
const username = process.env.RELEASE_GATE_SCENARIO_USERNAME ?? 'john';
const password =
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env node
/**
* Local headroom proxy smoke (loopback only).
* Requires: headroom proxy running with upstream pointing at DeepSeek compat proxy.
*
* Example:
* OPENAI_BASE_URL=http://127.0.0.1:18036/v1 HEADROOM_OUTPUT_SHAPER=0 headroom proxy --port 8787
*/
import {
buildHeadroomRunObservation,
probeHeadroomProxyReachable,
resolveHeadroomMode,
resolveHeadroomProxyBaseUrl,
resolveHeadroomUpstreamBaseUrl,
} from '../memind-headroom-policy.mjs';
const mode = resolveHeadroomMode(process.env);
if (mode === 'off') {
console.log('HEADROOM_LOCAL_SKIP: MEMIND_HEADROOM_MODE=off');
process.exit(0);
}
const reachable = await probeHeadroomProxyReachable();
const observation = buildHeadroomRunObservation({ skillId: 'web' });
console.log('HEADROOM_LOCAL_PROBE:');
console.log(` mode=${mode}`);
console.log(` proxy=${resolveHeadroomProxyBaseUrl()}`);
console.log(` upstream=${resolveHeadroomUpstreamBaseUrl()}`);
console.log(` reachable=${reachable}`);
console.log(` observation=${JSON.stringify(observation)}`);
if (!reachable) {
console.error('HEADROOM_LOCAL_FAIL: proxy unreachable');
process.exit(1);
}
console.log('HEADROOM_LOCAL_OK');
+12 -1
View File
@@ -1,5 +1,6 @@
import path from 'node:path';
import { developerToolsFromPolicy } from './capabilities.mjs';
import { applyContextBudgetToHarnessEntries } from './context-budget.mjs';
import { buildSessionMemoryEntries } from './user-memory-profile.mjs';
import { buildSandboxSessionConstraints } from './user-publish.mjs';
@@ -307,13 +308,23 @@ export async function reconcileAgentSession(
})
: null;
const memoryEntries = buildSessionMemoryEntries({
const rawMemoryEntries = buildSessionMemoryEntries({
workingDir,
sessionPolicy,
sandboxConstraints: sandboxText,
userContext,
userMemories,
});
const { entries: memoryEntries, plan: harnessBudgetPlan } = applyContextBudgetToHarnessEntries(
rawMemoryEntries,
);
if (harnessBudgetPlan?.mode === 'shadow' && harnessBudgetPlan.duplicateItems.length > 0) {
console.log('[session-reconcile] context budget shadow', {
duplicateCount: harnessBudgetPlan.duplicateItems.length,
droppedCount: harnessBudgetPlan.droppedItems.length,
keptBlockCount: harnessBudgetPlan.keptBlockCount,
});
}
if (memoryEntries.length > 0) {
for (const entry of memoryEntries) {