newmark-agent 0.6.4 → 0.6.7
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/config.example.json +13 -3
- package/dist/cli-commands.d.ts +1 -1
- package/dist/cli-commands.js +53 -2
- package/dist/cli-discovery.js +3 -0
- package/dist/conversation-utility-host.bundle.cjs +2880 -1069
- package/dist/conversation-utility-host.js +94 -7
- package/dist/core/agent.d.ts +126 -12
- package/dist/core/agent.js +783 -172
- package/dist/core/agentKernelRunner.d.ts +7 -1
- package/dist/core/agentKernelRunner.js +156 -20
- package/dist/core/autoRouter.d.ts +49 -44
- package/dist/core/autoRouter.js +117 -302
- package/dist/core/config.js +27 -11
- package/dist/core/continuation/contracts.d.ts +1 -1
- package/dist/core/continuation/store.d.ts +91 -1
- package/dist/core/continuation/store.js +291 -61
- package/dist/core/conversationKernel.d.ts +49 -7
- package/dist/core/conversationKernel.js +303 -51
- package/dist/core/conversationStateDocument.d.ts +18 -0
- package/dist/core/conversationStateDocument.js +88 -0
- package/dist/core/conversationVisualBudget.d.ts +74 -0
- package/dist/core/conversationVisualBudget.js +153 -0
- package/dist/core/electronUtilityAgentClient.d.ts +3 -2
- package/dist/core/electronUtilityAgentClient.js +32 -8
- package/dist/core/electronUtilityRuntimePool.d.ts +28 -3
- package/dist/core/electronUtilityRuntimePool.js +93 -24
- package/dist/core/hostRuntimeHooks.d.ts +23 -0
- package/dist/core/hostRuntimeHooks.js +17 -0
- package/dist/core/jevDecision.d.ts +144 -0
- package/dist/core/jevDecision.js +226 -0
- package/dist/core/memoryProbe.d.ts +24 -0
- package/dist/core/memoryProbe.js +104 -0
- package/dist/core/mobilePairing.d.ts +7 -1
- package/dist/core/mobilePairing.js +9 -1
- package/dist/core/performanceDiagnostics.d.ts +1 -1
- package/dist/core/routeDecisionValidator.d.ts +86 -0
- package/dist/core/routeDecisionValidator.js +249 -0
- package/dist/core/routeEligibility.d.ts +52 -0
- package/dist/core/routeEligibility.js +108 -0
- package/dist/core/runtimeMemoryBudget.d.ts +6 -0
- package/dist/core/runtimeMemoryBudget.js +12 -0
- package/dist/core/utilityAgentProtocol.d.ts +8 -10
- package/dist/core/utilityHostToolRouter.js +2 -0
- package/dist/core/visualDownscale.d.ts +6 -0
- package/dist/core/visualDownscale.js +137 -0
- package/dist/core/wslAgentClient.d.ts +1 -2
- package/dist/core/wslAgentClient.js +0 -8
- package/dist/core/wslAgentProtocol.d.ts +1 -10
- package/dist/core/wslAgentRuntimePool.d.ts +1 -3
- package/dist/core/wslAgentRuntimePool.js +17 -22
- package/dist/llm/provider.d.ts +3 -1
- package/dist/llm/provider.js +53 -9
- package/dist/main.js +107 -30
- package/dist/preload.js +3 -1
- package/dist/server.js +30 -18
- package/dist/tools/computerUse.d.ts +4 -0
- package/dist/tools/computerUse.js +202 -4
- package/dist/tools/computerUsePowerShellHost.d.ts +6 -1
- package/dist/tools/computerUsePowerShellHost.js +105 -31
- package/dist/tools/index.js +4 -1
- package/dist/tui/src/adapters/core-runtime-adapter.js +13 -1
- package/dist/tui/src/i18n.js +12 -1
- package/dist/tui/src/render.js +9 -2
- package/dist/tui/src/settings-schema.js +19 -0
- package/dist/tui/src/state.js +69 -1
- package/dist/ui/index.html +651 -233
- package/dist/ui/lucide-sprite.svg +0 -8
- package/dist/wsl-agent-host.bundle.cjs +2685 -1044
- package/dist/wsl-agent-host.js +0 -3
- package/package.json +36 -9
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Agent } from './agent';
|
|
2
2
|
import { StreamToken } from './types';
|
|
3
3
|
import { type ToolchainCore } from '../toolchain';
|
|
4
|
+
import { type ConversationVisualBudget } from './conversationVisualBudget';
|
|
4
5
|
export declare function filterPublicAssistantDelta(agent: Agent, delta: string): string;
|
|
5
6
|
export declare function resetPublicAssistantDeltaFilter(agent: Agent): void;
|
|
6
7
|
interface KernelTextContent {
|
|
@@ -143,7 +144,12 @@ declare function visualFallbackImageInput(agent: Agent, name: string, text: stri
|
|
|
143
144
|
export declare function toolResultObjectiveOutcome(result: string): boolean | undefined;
|
|
144
145
|
declare function toKernelMessagesFromHistory(history: Array<Record<string, unknown>>, agent: Agent): KernelMessage[];
|
|
145
146
|
declare function toProviderToolDefinitions(tools: KernelTool[]): unknown[];
|
|
146
|
-
declare function fromKernelMessages(messages: KernelMessage[], includeEphemeralImages?: boolean
|
|
147
|
+
declare function fromKernelMessages(messages: KernelMessage[], includeEphemeralImages?: boolean, visualBudget?: ConversationVisualBudget, onVisualRelease?: (info: {
|
|
148
|
+
releasedImages: number;
|
|
149
|
+
releasedBytes: number;
|
|
150
|
+
retainedBytes: number;
|
|
151
|
+
budget: ConversationVisualBudget;
|
|
152
|
+
}) => void): Array<Record<string, unknown>>;
|
|
147
153
|
declare function imagePathToOpenAIContentPart(imagePath: string): Record<string, unknown> | null;
|
|
148
154
|
export declare const agentKernelRunnerInternals: {
|
|
149
155
|
buildRequestTaskFocus: typeof buildRequestTaskFocus;
|
|
@@ -48,8 +48,13 @@ const toolPolicy_1 = require("./toolPolicy");
|
|
|
48
48
|
const performanceDiagnostics_1 = require("./performanceDiagnostics");
|
|
49
49
|
const agentKernelDiagnostics_1 = require("./agentKernelDiagnostics");
|
|
50
50
|
const toolchain_1 = require("../toolchain");
|
|
51
|
+
const conversationVisualBudget_1 = require("./conversationVisualBudget");
|
|
52
|
+
const visualDownscale_1 = require("./visualDownscale");
|
|
51
53
|
const emptyResponseRetry_1 = require("./emptyResponseRetry");
|
|
52
54
|
const publicStreamFilters = new WeakMap();
|
|
55
|
+
/** dev-0.6.6: throttles the "screenshots released" work-status line. */
|
|
56
|
+
let visualReleaseReportedAt = 0;
|
|
57
|
+
const VISUAL_MIB = 1024 ** 2;
|
|
53
58
|
const brokerOnlyAssistantBuffers = new WeakMap();
|
|
54
59
|
const BROKER_PREFACE_BUFFER_CHARS = 96;
|
|
55
60
|
const HIDDEN_LINE_PREFIXES = [
|
|
@@ -460,7 +465,11 @@ async function runAgentKernel(agent) {
|
|
|
460
465
|
agent.chatMessages.push({ role: 'user', content: KernelMessageText(msg), mode: agent.modeName(), model: agent.model, timestamp: agent.nowLabel() });
|
|
461
466
|
agent.history.push(toHistoryMessage(msg));
|
|
462
467
|
}
|
|
463
|
-
|
|
468
|
+
// Mid-turn persistence is coalesced (see Agent.scheduleStoredConversationState):
|
|
469
|
+
// the terminal save at the end of the run still writes synchronously, so
|
|
470
|
+
// durability at turn boundaries is unchanged while per-turn full-document
|
|
471
|
+
// rewrites drop from one-per-event to at most one per throttle window.
|
|
472
|
+
agent.saveWorkspaceConversationState(false);
|
|
464
473
|
}
|
|
465
474
|
await kernel.prompt(promptMessages);
|
|
466
475
|
const assistant = lastAssistant;
|
|
@@ -519,7 +528,7 @@ async function runAgentKernel(agent) {
|
|
|
519
528
|
agent.emitWorkEvent({ type: 'final_response', content: visualFallback });
|
|
520
529
|
agent.chatMessages.push({ role: 'assistant', content: visualFallback, mode: agent.modeName(), model: agent.model, timestamp: agent.nowLabel(), runId: agent.currentWorkRunId() || undefined });
|
|
521
530
|
agent.history.push({ role: 'assistant', content: visualFallback, run_id: agent.currentWorkRunId() || undefined });
|
|
522
|
-
agent.saveWorkspaceConversationState();
|
|
531
|
+
agent.saveWorkspaceConversationState(false);
|
|
523
532
|
agent.recordWorkStatus('Final visual fallback used: local mini OCR plus conservative text correction.');
|
|
524
533
|
lastTurn = { ...lastTurn, text: visualFallback, errorMessage: '', stopReason: 'stop' };
|
|
525
534
|
}
|
|
@@ -658,7 +667,9 @@ async function runAgentKernel(agent) {
|
|
|
658
667
|
agent.attachAgentKernelRuntime(null);
|
|
659
668
|
}
|
|
660
669
|
agent.status = 'idle';
|
|
661
|
-
|
|
670
|
+
// Coalesced: the caller (Agent.process) flushes synchronously right after,
|
|
671
|
+
// and every in-memory reader already sees this status.
|
|
672
|
+
agent.saveWorkspaceConversationState(false);
|
|
662
673
|
return agent.sanitizeVisibleTokens(tokens);
|
|
663
674
|
function streamWithNewmarkProvider(currentAgent, compat) {
|
|
664
675
|
return async (model, context, options) => {
|
|
@@ -696,7 +707,15 @@ async function runAgentKernel(agent) {
|
|
|
696
707
|
// endpoint owns its real limit; protocols that require a positive
|
|
697
708
|
// max_tokens resolve one internally.
|
|
698
709
|
const maxTokens = 0;
|
|
699
|
-
const
|
|
710
|
+
const requestVisualBudget = visualBudgetForAgent(currentAgent);
|
|
711
|
+
const newmarkMessages = fromKernelMessages(context.messages, true, requestVisualBudget, info => {
|
|
712
|
+
// Observable, throttled: one status line per run so the user learns
|
|
713
|
+
// that screenshots were reclaimed instead of silently losing them.
|
|
714
|
+
if (visualReleaseReportedAt && Date.now() - visualReleaseReportedAt < 60_000)
|
|
715
|
+
return;
|
|
716
|
+
visualReleaseReportedAt = Date.now();
|
|
717
|
+
currentAgent.recordWorkStatus(`Released ${info.releasedImages} older ComputerUse/BrowserControl screenshot(s) (~${Math.round(info.releasedBytes / VISUAL_MIB)} MiB) to stay within the ${(0, conversationVisualBudget_1.describeVisualBudget)(info.budget)} budget.`);
|
|
718
|
+
}).map(message => message.role === 'assistant'
|
|
700
719
|
// Use the same public-content boundary on its first provider
|
|
701
720
|
// submission and after persistence. Trimming only the durable
|
|
702
721
|
// copy changes earlier tool-call envelopes on mailbox recovery.
|
|
@@ -1051,7 +1070,7 @@ async function handleKernelEvent(agent, event, tokens) {
|
|
|
1051
1070
|
// Otherwise a cold continuation loses an already-read instruction
|
|
1052
1071
|
// and deletes that message from the provider's retained prefix.
|
|
1053
1072
|
agent.history.push({ ...toHistoryMessage(event.message), run_id: agent.currentWorkRunId() || undefined });
|
|
1054
|
-
agent.saveWorkspaceConversationState(
|
|
1073
|
+
agent.saveWorkspaceConversationState(false);
|
|
1055
1074
|
}
|
|
1056
1075
|
agent.notifyAgentKernelUserMessageStart(text, event.message.clientMessageId);
|
|
1057
1076
|
}
|
|
@@ -1158,7 +1177,7 @@ async function handleKernelEvent(agent, event, tokens) {
|
|
|
1158
1177
|
historyMessage.content = text;
|
|
1159
1178
|
historyMessage.run_id = agent.currentWorkRunId() || undefined;
|
|
1160
1179
|
agent.history.push(historyMessage);
|
|
1161
|
-
agent.saveWorkspaceConversationState();
|
|
1180
|
+
agent.saveWorkspaceConversationState(false);
|
|
1162
1181
|
}
|
|
1163
1182
|
resetPublicAssistantDeltaFilter(agent);
|
|
1164
1183
|
resetAssistantToolVisibility(agent);
|
|
@@ -1169,6 +1188,9 @@ async function handleKernelEvent(agent, event, tokens) {
|
|
|
1169
1188
|
...(!event.message.isError && receipt ? { subagent_settlement_receipt: { ...receipt } } : {}),
|
|
1170
1189
|
};
|
|
1171
1190
|
agent.history.push(historyMessage);
|
|
1191
|
+
// Kept synchronous on purpose: the catch below clears the settlement
|
|
1192
|
+
// receipt so a later notification cannot mistake an unsaved result for a
|
|
1193
|
+
// committed one, which requires the write failure to surface here.
|
|
1172
1194
|
try {
|
|
1173
1195
|
agent.saveWorkspaceConversationState();
|
|
1174
1196
|
}
|
|
@@ -1790,8 +1812,17 @@ function visualFallbackImageInput(agent, name, text) {
|
|
|
1790
1812
|
if (name !== 'pdf_read' && nested.action !== 'observe' && nested.action !== 'app_observe')
|
|
1791
1813
|
return {};
|
|
1792
1814
|
const directImage = String(nested.vision_image_data_url || '');
|
|
1793
|
-
if (/^data:image\/(?:png|jpeg);base64,[A-Za-z0-9+/]+={0,2}$/i.test(directImage)
|
|
1794
|
-
|
|
1815
|
+
if (/^data:image\/(?:png|jpeg);base64,[A-Za-z0-9+/]+={0,2}$/i.test(directImage)) {
|
|
1816
|
+
// dev-0.6.6: ComputerUse/BrowserControl screenshots are the dominant
|
|
1817
|
+
// memory consumer of a long conversation, so the accepted single-image
|
|
1818
|
+
// size follows the machine's available memory instead of a fixed 2 MiB.
|
|
1819
|
+
const budget = visualBudgetForAgent(agent);
|
|
1820
|
+
const fitted = (0, conversationVisualBudget_1.visualDataUrlBytes)(directImage) <= budget.maxSingleImageBytes
|
|
1821
|
+
? directImage
|
|
1822
|
+
: (0, visualDownscale_1.downscaleDataUrlToBytes)(directImage, budget.maxSingleImageBytes);
|
|
1823
|
+
if (fitted)
|
|
1824
|
+
return { image: fitted, mimeType: fitted.slice(5, fitted.indexOf(';')).toLowerCase() };
|
|
1825
|
+
return {};
|
|
1795
1826
|
}
|
|
1796
1827
|
const screenshotPath = String(nested.vision_image_path || '');
|
|
1797
1828
|
if (!screenshotPath)
|
|
@@ -1803,6 +1834,47 @@ function visualFallbackImageInput(agent, name, text) {
|
|
|
1803
1834
|
return {};
|
|
1804
1835
|
}
|
|
1805
1836
|
}
|
|
1837
|
+
const visualBudgetCache = new WeakMap();
|
|
1838
|
+
/**
|
|
1839
|
+
* The conversation's visual budget for right now. Sampled at most once per
|
|
1840
|
+
* second per agent so a tool-heavy round does not re-read OS memory state on
|
|
1841
|
+
* every screenshot.
|
|
1842
|
+
*/
|
|
1843
|
+
function visualBudgetForAgent(agent) {
|
|
1844
|
+
const cached = visualBudgetCache.get(agent);
|
|
1845
|
+
const now = Date.now();
|
|
1846
|
+
if (cached && now - cached.at < 1000)
|
|
1847
|
+
return cached.budget;
|
|
1848
|
+
const usage = (() => {
|
|
1849
|
+
try {
|
|
1850
|
+
return process.memoryUsage();
|
|
1851
|
+
}
|
|
1852
|
+
catch {
|
|
1853
|
+
return { rss: 0 };
|
|
1854
|
+
}
|
|
1855
|
+
})();
|
|
1856
|
+
const budget = (0, conversationVisualBudget_1.conversationVisualBudget)({ rssBytes: Number(usage?.rss || 0) });
|
|
1857
|
+
visualBudgetCache.set(agent, { at: now, budget });
|
|
1858
|
+
return budget;
|
|
1859
|
+
}
|
|
1860
|
+
/** Bytes an image part costs when materialized into the provider request. */
|
|
1861
|
+
function imagePartBytes(part) {
|
|
1862
|
+
const inline = String(part.image || '');
|
|
1863
|
+
if (inline.startsWith('data:'))
|
|
1864
|
+
return (0, conversationVisualBudget_1.visualDataUrlBytes)(inline);
|
|
1865
|
+
const imagePath = String(part.imagePath || '');
|
|
1866
|
+
if (!imagePath)
|
|
1867
|
+
return 0;
|
|
1868
|
+
try {
|
|
1869
|
+
// Materializing a path-based screenshot produces `data:<mime>;base64,<...>`;
|
|
1870
|
+
// budget the base64 payload length the request will actually hold.
|
|
1871
|
+
const size = fs.statSync(imagePath).size;
|
|
1872
|
+
return 4 * Math.ceil(size / 3);
|
|
1873
|
+
}
|
|
1874
|
+
catch {
|
|
1875
|
+
return 0;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1806
1878
|
async function executeNewmarkTool(agent, name, args, inputSchema, signal, onSettlementReceipt) {
|
|
1807
1879
|
const stopToolTimer = (0, performanceDiagnostics_1.performanceTimer)('tool_execution', { conversationId: agent.activeConversationId, detail: { tool: name } });
|
|
1808
1880
|
try {
|
|
@@ -1953,8 +2025,11 @@ async function executeNewmarkTool(agent, name, args, inputSchema, signal, onSett
|
|
|
1953
2025
|
throw abortError();
|
|
1954
2026
|
trackFileDiff(agent, name, args);
|
|
1955
2027
|
const objectiveResult = toolResultObjectiveOutcome(result);
|
|
2028
|
+
// Objective postconditions are operational health evidence only. Auto Router
|
|
2029
|
+
// v2 keeps tool outcomes out of model preference: the observation can demote
|
|
2030
|
+
// an endpoint's live health, and it can never teach a future route.
|
|
1956
2031
|
if (objectiveResult !== undefined)
|
|
1957
|
-
agent.
|
|
2032
|
+
agent.recordRouteToolOutcome(objectiveResult);
|
|
1958
2033
|
return result;
|
|
1959
2034
|
}
|
|
1960
2035
|
finally {
|
|
@@ -2081,9 +2156,53 @@ function toProviderToolDefinitions(tools) {
|
|
|
2081
2156
|
},
|
|
2082
2157
|
}));
|
|
2083
2158
|
}
|
|
2084
|
-
function fromKernelMessages(messages, includeEphemeralImages = true) {
|
|
2085
|
-
|
|
2086
|
-
|
|
2159
|
+
function fromKernelMessages(messages, includeEphemeralImages = true, visualBudget, onVisualRelease) {
|
|
2160
|
+
const budget = visualBudget || (0, conversationVisualBudget_1.conversationVisualBudget)();
|
|
2161
|
+
// dev-0.6.6: a late-stage ComputerUse/BrowserControl conversation stores one
|
|
2162
|
+
// screenshot per observation. Materializing all of them per request is what
|
|
2163
|
+
// pushed the utility host to a 3 GB V8 abort, so the newest screenshots are
|
|
2164
|
+
// kept within this machine's visual budget and older ones are released
|
|
2165
|
+
// (text/tool results stay untouched).
|
|
2166
|
+
const candidates = [];
|
|
2167
|
+
messages.forEach((message, index) => {
|
|
2168
|
+
if (message.role !== 'toolResult')
|
|
2169
|
+
return;
|
|
2170
|
+
const image = message.content.find((part) => part.type === 'image');
|
|
2171
|
+
if (!image)
|
|
2172
|
+
return;
|
|
2173
|
+
candidates.push({ key: String(index), bytes: imagePartBytes(image) });
|
|
2174
|
+
});
|
|
2175
|
+
const plan = (0, conversationVisualBudget_1.planVisualRetention)(candidates, budget);
|
|
2176
|
+
if (plan.released.length && onVisualRelease) {
|
|
2177
|
+
onVisualRelease({
|
|
2178
|
+
releasedImages: plan.released.length,
|
|
2179
|
+
releasedBytes: plan.releasedBytes,
|
|
2180
|
+
retainedBytes: plan.retainedBytes,
|
|
2181
|
+
budget,
|
|
2182
|
+
});
|
|
2183
|
+
}
|
|
2184
|
+
return messages.flatMap((message, index) => {
|
|
2185
|
+
const key = String(index);
|
|
2186
|
+
const hadImage = message.role === 'toolResult'
|
|
2187
|
+
&& message.content.some(part => part.type === 'image');
|
|
2188
|
+
const imageAllowed = !hadImage || plan.keep.has(key);
|
|
2189
|
+
const projected = toHistoryMessage(message, includeEphemeralImages && imageAllowed, budget);
|
|
2190
|
+
if (hadImage && !imageAllowed) {
|
|
2191
|
+
// Tell the model the screenshot existed but was released, so it can
|
|
2192
|
+
// re-observe instead of assuming the picture was empty.
|
|
2193
|
+
// Kept to one short line: a long conversation can release hundreds of
|
|
2194
|
+
// historic screenshots, and the note must not become its own payload.
|
|
2195
|
+
const releaseNote = `[visual_memory_budget] screenshot released (${budget.pressure} memory budget); re-run observe if you need it again.`;
|
|
2196
|
+
if (typeof projected.content === 'string')
|
|
2197
|
+
projected.content = `${projected.content}\n${releaseNote}`;
|
|
2198
|
+
else if (Array.isArray(projected.content)) {
|
|
2199
|
+
const textPart = projected.content.find(part => part?.type === 'text');
|
|
2200
|
+
if (textPart)
|
|
2201
|
+
textPart.text = `${String(textPart.text || '')}\n${releaseNote}`;
|
|
2202
|
+
else
|
|
2203
|
+
projected.content.unshift({ type: 'text', text: releaseNote });
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2087
2206
|
if (message.role !== 'toolResult' || !Array.isArray(projected.content))
|
|
2088
2207
|
return [projected];
|
|
2089
2208
|
const parts = projected.content;
|
|
@@ -2122,7 +2241,7 @@ function publicHistoryFromKernelMessages(messages) {
|
|
|
2122
2241
|
return [toHistoryMessage({ ...message, content: publicContent }, false)];
|
|
2123
2242
|
});
|
|
2124
2243
|
}
|
|
2125
|
-
function toHistoryMessage(message, includeEphemeralImages = false) {
|
|
2244
|
+
function toHistoryMessage(message, includeEphemeralImages = false, visualBudget) {
|
|
2126
2245
|
if (message.role === 'user') {
|
|
2127
2246
|
if (typeof message.content === 'string')
|
|
2128
2247
|
return {
|
|
@@ -2154,13 +2273,7 @@ function toHistoryMessage(message, includeEphemeralImages = false) {
|
|
|
2154
2273
|
const ephemeralImage = message.content.find((c) => c.type === 'image');
|
|
2155
2274
|
const imagePath = ephemeralImage?.imagePath || '';
|
|
2156
2275
|
const directImage = ephemeralImage?.image || '';
|
|
2157
|
-
const imagePart = includeEphemeralImages
|
|
2158
|
-
? (imagePath
|
|
2159
|
-
? imagePathToOpenAIContentPart(imagePath)
|
|
2160
|
-
: directImage.startsWith('data:image/')
|
|
2161
|
-
? { type: 'image_url', image_url: { url: directImage } }
|
|
2162
|
-
: null)
|
|
2163
|
-
: null;
|
|
2276
|
+
const imagePart = includeEphemeralImages ? materializeVisualImagePart(imagePath, directImage, visualBudget) : null;
|
|
2164
2277
|
if (imagePart && ephemeralImage) {
|
|
2165
2278
|
// Tool-result images are one provider-input capability, not history. Once
|
|
2166
2279
|
// this request has materialized the image part, consume it from the live
|
|
@@ -2186,6 +2299,29 @@ function imagePathToOpenAIContentPart(imagePath) {
|
|
|
2186
2299
|
return null;
|
|
2187
2300
|
}
|
|
2188
2301
|
}
|
|
2302
|
+
/**
|
|
2303
|
+
* dev-0.6.6: materialize one screenshot into a provider image part while
|
|
2304
|
+
* respecting the conversation's per-image cap. Oversized screenshots are
|
|
2305
|
+
* downscaled (PNG, then JPEG) instead of being dropped, so a memory-tight
|
|
2306
|
+
* machine keeps vision and the agent keeps working.
|
|
2307
|
+
*/
|
|
2308
|
+
function materializeVisualImagePart(imagePath, directImage, visualBudget) {
|
|
2309
|
+
const budget = visualBudget || (0, conversationVisualBudget_1.conversationVisualBudget)();
|
|
2310
|
+
let dataUrl = '';
|
|
2311
|
+
if (String(directImage || '').startsWith('data:image/'))
|
|
2312
|
+
dataUrl = String(directImage);
|
|
2313
|
+
else if (imagePath)
|
|
2314
|
+
dataUrl = imagePathToDataUrl(imagePath) || '';
|
|
2315
|
+
if (!dataUrl)
|
|
2316
|
+
return null;
|
|
2317
|
+
if ((0, conversationVisualBudget_1.visualDataUrlBytes)(dataUrl) > budget.maxSingleImageBytes) {
|
|
2318
|
+
const fitted = (0, visualDownscale_1.downscaleDataUrlToBytes)(dataUrl, budget.maxSingleImageBytes);
|
|
2319
|
+
if (!fitted)
|
|
2320
|
+
return null;
|
|
2321
|
+
dataUrl = fitted;
|
|
2322
|
+
}
|
|
2323
|
+
return { type: 'image_url', image_url: { url: dataUrl } };
|
|
2324
|
+
}
|
|
2189
2325
|
exports.agentKernelRunnerInternals = {
|
|
2190
2326
|
buildRequestTaskFocus,
|
|
2191
2327
|
buildBuildContextBootstrap,
|
|
@@ -18,24 +18,29 @@ export type ModelSelection = {
|
|
|
18
18
|
policyId: string;
|
|
19
19
|
subset?: DeploymentRef[];
|
|
20
20
|
};
|
|
21
|
-
export type RouteMode = '
|
|
21
|
+
export type RouteMode = 'conservative' | 'balanced' | 'aggressive';
|
|
22
22
|
export type RoutePrivacy = 'default' | 'no_training' | 'zdr';
|
|
23
23
|
export type TaskClass = 'chat' | 'coding' | 'reasoning' | 'long_context' | 'vision' | 'image_generation' | 'tool_use' | 'computer_use';
|
|
24
24
|
export type ValidationLevel = 'discovered' | 'legacy_basic' | 'basic' | 'standard' | 'extended';
|
|
25
25
|
export type ValidationStatus = 'verified' | 'degraded' | 'unavailable' | 'auth_error' | 'rate_limited' | 'invalid_config';
|
|
26
26
|
export interface RoutePolicy {
|
|
27
27
|
mode: RouteMode;
|
|
28
|
-
maxQualityLoss: number;
|
|
29
|
-
maxExpectedCostUsd?: number;
|
|
30
28
|
allowPreview: boolean;
|
|
31
29
|
privacy: RoutePrivacy;
|
|
32
30
|
requiredCapabilities: string[];
|
|
33
31
|
dataRegion?: string;
|
|
34
32
|
requiredProtocolParameters?: string[];
|
|
33
|
+
maxExpectedCostUsd?: number;
|
|
35
34
|
}
|
|
36
35
|
export interface AutoRouteCandidate {
|
|
37
36
|
deployment: DeploymentRef;
|
|
38
37
|
enabled: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Set by the Agent when a deployment is explicitly unusable right now
|
|
40
|
+
* (for example a provider-reported balance exhaustion window). It carries a
|
|
41
|
+
* guard reason instead of silently mutating `enabled`.
|
|
42
|
+
*/
|
|
43
|
+
unavailableReason?: string;
|
|
39
44
|
validation: {
|
|
40
45
|
level: ValidationLevel;
|
|
41
46
|
status: ValidationStatus;
|
|
@@ -49,39 +54,24 @@ export interface AutoRouteCandidate {
|
|
|
49
54
|
supportedProtocolParameters?: string[];
|
|
50
55
|
expectedInputCostUsdPerM?: number;
|
|
51
56
|
expectedOutputCostUsdPerM?: number;
|
|
57
|
+
/** Current operational measurements. Never persisted as a preference. */
|
|
52
58
|
latencyMs?: number;
|
|
53
59
|
reliability?: number;
|
|
54
60
|
toolValidity?: number;
|
|
55
61
|
throughput?: number;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
attempts: number;
|
|
59
|
-
}>>;
|
|
62
|
+
circuit?: 'closed' | 'open' | 'half_open';
|
|
63
|
+
/** Explicit user configuration (`route_preference`), not learned history. */
|
|
60
64
|
preference?: number;
|
|
61
65
|
fallbackOnly?: boolean;
|
|
62
66
|
}
|
|
63
67
|
export interface RouteRequest {
|
|
64
68
|
transactionId: string;
|
|
65
|
-
affinityKey: string;
|
|
66
69
|
taskText: string;
|
|
67
70
|
estimatedInputTokens: number;
|
|
68
71
|
expectedOutputTokens: number;
|
|
69
72
|
requiredCapabilities: string[];
|
|
70
73
|
batch?: boolean;
|
|
71
74
|
}
|
|
72
|
-
export interface RankedRouteCandidate {
|
|
73
|
-
deployment: DeploymentRef;
|
|
74
|
-
quality: number;
|
|
75
|
-
utility: number;
|
|
76
|
-
expectedCostUsd?: number;
|
|
77
|
-
components: {
|
|
78
|
-
cost: number;
|
|
79
|
-
reliability: number;
|
|
80
|
-
speed: number;
|
|
81
|
-
cache: number;
|
|
82
|
-
preference: number;
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
75
|
export type RouteAttemptStatus = 'planned' | 'success' | 'failed' | 'blocked';
|
|
86
76
|
export interface RouteAttempt {
|
|
87
77
|
deployment: DeploymentRef;
|
|
@@ -97,15 +87,24 @@ export interface RouteDecision {
|
|
|
97
87
|
routeId: string;
|
|
98
88
|
requestedSelection: ModelSelection;
|
|
99
89
|
policyVersion: string;
|
|
90
|
+
decisionSource: 'jev';
|
|
100
91
|
catalogSnapshotHash: string;
|
|
101
92
|
taskClasses: TaskClass[];
|
|
102
93
|
excludedCandidates: Array<{
|
|
103
94
|
deployment: DeploymentRef;
|
|
104
95
|
reasons: string[];
|
|
105
96
|
}>;
|
|
106
|
-
rankedCandidates: RankedRouteCandidate[];
|
|
107
97
|
resolvedDeployment?: DeploymentRef;
|
|
108
|
-
|
|
98
|
+
/** Ordered recovery hints produced by the decision engine, already validated. */
|
|
99
|
+
alternatives: DeploymentRef[];
|
|
100
|
+
jev: {
|
|
101
|
+
modelVersion: string;
|
|
102
|
+
confidence?: number;
|
|
103
|
+
reasonCodes: string[];
|
|
104
|
+
latencyMs: number;
|
|
105
|
+
};
|
|
106
|
+
decisionError?: string;
|
|
107
|
+
pinReason?: 'transaction';
|
|
109
108
|
attempts: RouteAttempt[];
|
|
110
109
|
finalStatus: 'resolved' | 'no_candidate' | 'fixed_unavailable' | 'retrying' | 'succeeded' | 'failed' | 'blocked';
|
|
111
110
|
retryBudgetMs?: number;
|
|
@@ -121,34 +120,45 @@ export interface RouteFailure {
|
|
|
121
120
|
export interface PlannedRouteAttempt extends RouteAttempt {
|
|
122
121
|
status: 'planned';
|
|
123
122
|
}
|
|
124
|
-
export
|
|
125
|
-
deployment: DeploymentRef;
|
|
126
|
-
taskClass: TaskClass;
|
|
127
|
-
score: number;
|
|
128
|
-
source: 'manual_switch' | 'explicit_rating' | 'objective_success';
|
|
129
|
-
at?: number;
|
|
130
|
-
}
|
|
123
|
+
export declare const ROUTE_POLICY_VERSION = "newmark-auto-v2";
|
|
131
124
|
export declare function normalizeAutoPreference(value: string): RouteMode;
|
|
132
125
|
export declare function defaultRoutePolicy(mode?: RouteMode): RoutePolicy;
|
|
133
126
|
export declare function classifyTaskClasses(taskText: string, requiredCapabilities: string[], estimatedInputTokens?: number): TaskClass[];
|
|
134
127
|
export declare function classifyRouteFailure(error: unknown): RouteFailure;
|
|
135
|
-
export declare
|
|
128
|
+
export declare function createRouteId(): string;
|
|
129
|
+
export declare function catalogSnapshotHash(candidates: AutoRouteCandidate[]): string;
|
|
130
|
+
/**
|
|
131
|
+
* Operational execution controller.
|
|
132
|
+
*
|
|
133
|
+
* It owns endpoint health, the circuit breaker, build-scoped transaction pins
|
|
134
|
+
* and the provider-local recovery ladder. It never ranks models and never
|
|
135
|
+
* learns a preference; health observations only decide whether an endpoint is
|
|
136
|
+
* attempted right now.
|
|
137
|
+
*/
|
|
138
|
+
export declare class RouteExecutionController {
|
|
136
139
|
private readonly now;
|
|
137
140
|
private readonly policyVersion;
|
|
138
|
-
private readonly affinityTtlMs;
|
|
139
|
-
private readonly switchThreshold;
|
|
140
141
|
private readonly transactionPins;
|
|
141
|
-
private readonly affinities;
|
|
142
142
|
private readonly endpointHealth;
|
|
143
|
-
private readonly feedback;
|
|
144
143
|
constructor(options?: {
|
|
145
144
|
now?: () => number;
|
|
146
145
|
policyVersion?: string;
|
|
147
|
-
affinityTtlMs?: number;
|
|
148
|
-
switchThreshold?: number;
|
|
149
146
|
});
|
|
150
|
-
|
|
147
|
+
version(): string;
|
|
148
|
+
/** Execution-consistency pin for one Build / route transaction. */
|
|
149
|
+
pinTransaction(transactionId: string, deployment: DeploymentRef): void;
|
|
150
|
+
transactionPin(transactionId: string): DeploymentRef | undefined;
|
|
151
151
|
endTransaction(transactionId: string): void;
|
|
152
|
+
/**
|
|
153
|
+
* Provider-local recovery ladder.
|
|
154
|
+
*
|
|
155
|
+
* Order of preference: retry the same deployment (when the failure is
|
|
156
|
+
* retryable inside the retry budget), then an equivalent deployment of the
|
|
157
|
+
* same logical model group, then the alternates the decision engine already
|
|
158
|
+
* validated (in the order it produced them), then explicit fallback-only
|
|
159
|
+
* models. Recovery never crosses the failed provider boundary and never
|
|
160
|
+
* re-runs model selection.
|
|
161
|
+
*/
|
|
152
162
|
planAttempts(decision: RouteDecision, candidates: AutoRouteCandidate[], failure: {
|
|
153
163
|
error: RouteFailure;
|
|
154
164
|
streamCommitted: boolean;
|
|
@@ -170,14 +180,9 @@ export declare class AutoRouter {
|
|
|
170
180
|
toolValidity: number;
|
|
171
181
|
circuit: 'closed' | 'open' | 'half_open';
|
|
172
182
|
};
|
|
173
|
-
recordFeedback(event: RouteFeedbackEvent): boolean;
|
|
174
|
-
clearLearnedPreferences(): void;
|
|
175
|
-
learnedPreference(deployment: DeploymentRef, taskClasses: TaskClass[]): number;
|
|
176
|
-
private rankEligible;
|
|
177
|
-
private rankCandidate;
|
|
178
|
-
private affinityKey;
|
|
179
183
|
private healthFor;
|
|
180
184
|
private passedInitialHardFilters;
|
|
181
185
|
private circuitState;
|
|
182
186
|
}
|
|
187
|
+
export declare function validationEvidenceIsStale(checkedAt: string | undefined, now?: number): boolean;
|
|
183
188
|
//# sourceMappingURL=autoRouter.d.ts.map
|