newmark-agent 0.6.4 → 0.6.8
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 +54 -3
- package/dist/cli-discovery.js +3 -0
- package/dist/conversation-utility-host.bundle.cjs +4472 -1767
- package/dist/conversation-utility-host.js +94 -7
- package/dist/core/agent.d.ts +126 -12
- package/dist/core/agent.js +785 -172
- package/dist/core/agentKernelRunner.d.ts +7 -1
- package/dist/core/agentKernelRunner.js +160 -20
- package/dist/core/autoRouter.d.ts +49 -44
- package/dist/core/autoRouter.js +117 -302
- package/dist/core/computerUseSession.js +1 -1
- 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/fileWriteObservation.d.ts +41 -0
- package/dist/core/fileWriteObservation.js +454 -0
- 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/toolPolicy.d.ts +1 -1
- package/dist/core/toolPolicy.js +1 -1
- 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 +6 -0
- package/dist/tools/computerUse.js +406 -21
- package/dist/tools/computerUsePowerShellHost.d.ts +6 -1
- package/dist/tools/computerUsePowerShellHost.js +105 -31
- package/dist/tools/index.d.ts +3 -4
- package/dist/tools/index.js +146 -66
- 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 +4302 -1767
- package/dist/wsl-agent-host.js +0 -3
- package/package.json +39 -10
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JevDecisionValidationError = void 0;
|
|
4
|
+
exports.validateJevDecision = validateJevDecision;
|
|
5
|
+
exports.buildRouteDecision = buildRouteDecision;
|
|
6
|
+
exports.resolveAutoRouteDecision = resolveAutoRouteDecision;
|
|
7
|
+
const autoRouter_1 = require("./autoRouter");
|
|
8
|
+
const jevDecision_1 = require("./jevDecision");
|
|
9
|
+
const routeEligibility_1 = require("./routeEligibility");
|
|
10
|
+
class JevDecisionValidationError extends Error {
|
|
11
|
+
code;
|
|
12
|
+
constructor(code, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.name = 'JevDecisionValidationError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.JevDecisionValidationError = JevDecisionValidationError;
|
|
19
|
+
function validDeployment(deployment) {
|
|
20
|
+
if (!deployment || typeof deployment !== 'object')
|
|
21
|
+
return false;
|
|
22
|
+
const candidate = deployment;
|
|
23
|
+
return !!String(candidate.providerId || '').trim() && !!String(candidate.modelId || '').trim();
|
|
24
|
+
}
|
|
25
|
+
function normalizeDeployment(deployment) {
|
|
26
|
+
const logicalModelGroupId = String(deployment.logicalModelGroupId || '').trim();
|
|
27
|
+
return {
|
|
28
|
+
providerId: String(deployment.providerId),
|
|
29
|
+
modelId: String(deployment.modelId),
|
|
30
|
+
...(logicalModelGroupId ? { logicalModelGroupId } : {}),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Validate a decision engine output against the Guard result.
|
|
35
|
+
*
|
|
36
|
+
* The engine may only choose inside the space the Guard allowed. Alternates
|
|
37
|
+
* that leave that space are dropped (they are recovery hints, not primary
|
|
38
|
+
* decisions), while an ineligible primary is a hard failure: routing must fall
|
|
39
|
+
* back deterministically instead of silently executing something the Guard
|
|
40
|
+
* excluded.
|
|
41
|
+
*/
|
|
42
|
+
function validateJevDecision(decision, eligible, options = {}) {
|
|
43
|
+
if (!eligible.length) {
|
|
44
|
+
throw new JevDecisionValidationError('no_eligible_candidates', 'No eligible candidate was available for Auto selection');
|
|
45
|
+
}
|
|
46
|
+
if (!decision || typeof decision !== 'object') {
|
|
47
|
+
throw new JevDecisionValidationError('missing_primary', 'The decision engine returned no decision');
|
|
48
|
+
}
|
|
49
|
+
if (!validDeployment(decision.primary)) {
|
|
50
|
+
throw new JevDecisionValidationError('missing_primary', 'The decision engine returned no usable primary deployment');
|
|
51
|
+
}
|
|
52
|
+
const allowed = new Map(eligible.map(candidate => [
|
|
53
|
+
`${candidate.deployment.providerId}\u0000${candidate.deployment.modelId}`,
|
|
54
|
+
candidate,
|
|
55
|
+
]));
|
|
56
|
+
const primary = normalizeDeployment(decision.primary);
|
|
57
|
+
const allowedPrimary = allowed.get(`${primary.providerId}\u0000${primary.modelId}`);
|
|
58
|
+
if (!allowedPrimary) {
|
|
59
|
+
throw new JevDecisionValidationError('primary_not_eligible', `The decision engine selected ${primary.providerId}/${primary.modelId}, which the eligibility guard excluded`);
|
|
60
|
+
}
|
|
61
|
+
const maxAlternates = Math.max(0, options.maxAlternates ?? jevDecision_1.DEFAULT_JEV_MAX_ALTERNATES);
|
|
62
|
+
const alternates = [];
|
|
63
|
+
const droppedAlternates = [];
|
|
64
|
+
const rawAlternates = Array.isArray(decision.alternates) ? decision.alternates : [];
|
|
65
|
+
for (const raw of rawAlternates) {
|
|
66
|
+
if (!validDeployment(raw)) {
|
|
67
|
+
droppedAlternates.push({ deployment: { providerId: String(raw?.providerId || ''), modelId: String(raw?.modelId || '') }, reason: 'invalid_deployment' });
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const alternate = normalizeDeployment(raw);
|
|
71
|
+
if ((0, routeEligibility_1.sameDeploymentRef)(alternate, primary)) {
|
|
72
|
+
droppedAlternates.push({ deployment: alternate, reason: 'primary_duplicate' });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (!allowed.get(`${alternate.providerId}\u0000${alternate.modelId}`)) {
|
|
76
|
+
droppedAlternates.push({ deployment: alternate, reason: 'not_eligible' });
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (alternates.some(existing => (0, routeEligibility_1.sameDeploymentRef)(existing, alternate))) {
|
|
80
|
+
droppedAlternates.push({ deployment: alternate, reason: 'duplicate' });
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (alternates.length >= maxAlternates) {
|
|
84
|
+
droppedAlternates.push({ deployment: alternate, reason: 'over_limit' });
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
alternates.push(alternate);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
primary: { ...allowedPrimary.deployment, ...primary, ...(allowedPrimary.deployment.logicalModelGroupId && !primary.logicalModelGroupId
|
|
91
|
+
? { logicalModelGroupId: allowedPrimary.deployment.logicalModelGroupId }
|
|
92
|
+
: {}) },
|
|
93
|
+
alternates,
|
|
94
|
+
reasonCodes: Array.isArray(decision.reasonCodes) ? decision.reasonCodes.map(code => String(code)) : [],
|
|
95
|
+
...(typeof decision.confidence === 'number' && Number.isFinite(decision.confidence) ? { confidence: decision.confidence } : {}),
|
|
96
|
+
modelVersion: String(decision.modelVersion || 'unknown'),
|
|
97
|
+
droppedAlternates,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function buildRouteDecision(input) {
|
|
101
|
+
return {
|
|
102
|
+
routeId: input.routeId,
|
|
103
|
+
requestedSelection: input.selection,
|
|
104
|
+
policyVersion: input.policyVersion,
|
|
105
|
+
decisionSource: input.decisionSource,
|
|
106
|
+
catalogSnapshotHash: input.catalogSnapshotHash,
|
|
107
|
+
taskClasses: [...input.taskClasses],
|
|
108
|
+
excludedCandidates: input.excluded.map(entry => ({ deployment: { ...entry.deployment }, reasons: [...entry.reasons] })),
|
|
109
|
+
resolvedDeployment: { ...input.decision.primary },
|
|
110
|
+
alternatives: input.decision.alternates.map(deployment => ({ ...deployment })),
|
|
111
|
+
jev: input.jev,
|
|
112
|
+
...(input.decisionError ? { decisionError: input.decisionError } : {}),
|
|
113
|
+
...(input.pinReason ? { pinReason: input.pinReason } : {}),
|
|
114
|
+
attempts: input.attempts ? input.attempts.map(attempt => ({ ...attempt })) : [],
|
|
115
|
+
finalStatus: input.finalStatus || 'resolved',
|
|
116
|
+
...(input.retryBudgetMs === undefined ? {} : { retryBudgetMs: input.retryBudgetMs }),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The single Auto decision pipeline used by the Agent and by the verification
|
|
121
|
+
* suite. Keeping it here (instead of inline in the Agent) is what guarantees
|
|
122
|
+
* that tests exercise the shipping order of stages:
|
|
123
|
+
*
|
|
124
|
+
* guard → decision engine → validator → optional transaction pin
|
|
125
|
+
*/
|
|
126
|
+
async function resolveAutoRouteDecision(input) {
|
|
127
|
+
const { selection, policy, taskText, request, candidates, taskClasses, } = input;
|
|
128
|
+
const routeId = input.routeId || (0, autoRouter_1.createRouteId)();
|
|
129
|
+
const policyVersion = input.policyVersion || 'newmark-auto-v2';
|
|
130
|
+
const base = {
|
|
131
|
+
routeId,
|
|
132
|
+
policyVersion,
|
|
133
|
+
taskClasses,
|
|
134
|
+
retryBudgetMs: request.batch ? 15_000 : 5_000,
|
|
135
|
+
};
|
|
136
|
+
const eligibility = (0, routeEligibility_1.evaluateRouteEligibility)({ selection, policy, request, candidates });
|
|
137
|
+
const catalogHash = (0, autoRouter_1.catalogSnapshotHash)(candidates);
|
|
138
|
+
if (!eligibility.eligible.length) {
|
|
139
|
+
return {
|
|
140
|
+
routeId: base.routeId,
|
|
141
|
+
requestedSelection: selection,
|
|
142
|
+
policyVersion: base.policyVersion,
|
|
143
|
+
decisionSource: 'jev',
|
|
144
|
+
catalogSnapshotHash: catalogHash,
|
|
145
|
+
taskClasses,
|
|
146
|
+
excludedCandidates: eligibility.excluded,
|
|
147
|
+
alternatives: [],
|
|
148
|
+
jev: { modelVersion: jevDecision_1.JEV_ROUTE_MODEL_VERSION, reasonCodes: [...taskClasses, 'no_eligible_candidate'], latencyMs: 0 },
|
|
149
|
+
attempts: [],
|
|
150
|
+
finalStatus: 'no_candidate',
|
|
151
|
+
retryBudgetMs: base.retryBudgetMs,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const timeoutMs = Number.isFinite(input.timeoutMs) && Number(input.timeoutMs) > 0
|
|
155
|
+
? Math.min(5_000, Number(input.timeoutMs))
|
|
156
|
+
: jevDecision_1.DEFAULT_JEV_TIMEOUT_MS;
|
|
157
|
+
let decisionError = '';
|
|
158
|
+
let failureCode = '';
|
|
159
|
+
let validated = null;
|
|
160
|
+
let jevLatencyMs = 0;
|
|
161
|
+
if (input.engine) {
|
|
162
|
+
const jevRequest = (0, jevDecision_1.buildJevRouteRequest)({
|
|
163
|
+
transactionId: request.transactionId,
|
|
164
|
+
taskText,
|
|
165
|
+
taskClasses,
|
|
166
|
+
estimatedInputTokens: request.estimatedInputTokens,
|
|
167
|
+
expectedOutputTokens: request.expectedOutputTokens,
|
|
168
|
+
requiredCapabilities: request.requiredCapabilities,
|
|
169
|
+
intelligence: input.intelligence || 'default',
|
|
170
|
+
policy,
|
|
171
|
+
request,
|
|
172
|
+
candidates: eligibility.eligible,
|
|
173
|
+
previousDeployment: input.previousDeployment,
|
|
174
|
+
});
|
|
175
|
+
const outcome = await (0, jevDecision_1.decideWithinBudget)(input.engine, jevRequest, timeoutMs);
|
|
176
|
+
jevLatencyMs = outcome.latencyMs;
|
|
177
|
+
try {
|
|
178
|
+
if (!outcome.decision)
|
|
179
|
+
throw new Error(outcome.error?.message || 'The current model returned no JEV decision');
|
|
180
|
+
validated = validateJevDecision(outcome.decision, eligibility.eligible);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
failureCode = outcome.error?.code
|
|
184
|
+
|| (error instanceof JevDecisionValidationError ? error.code : 'invalid_output');
|
|
185
|
+
decisionError = `${failureCode}: ${error instanceof Error ? error.message : String(error)}`;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
failureCode = input.unavailableReason || 'runtime_unavailable';
|
|
190
|
+
decisionError = `${failureCode}: current-model JEV routing is not available`;
|
|
191
|
+
}
|
|
192
|
+
if (!validated) {
|
|
193
|
+
return {
|
|
194
|
+
routeId: base.routeId,
|
|
195
|
+
requestedSelection: selection,
|
|
196
|
+
policyVersion: base.policyVersion,
|
|
197
|
+
decisionSource: 'jev',
|
|
198
|
+
catalogSnapshotHash: catalogHash,
|
|
199
|
+
taskClasses,
|
|
200
|
+
excludedCandidates: eligibility.excluded,
|
|
201
|
+
alternatives: [],
|
|
202
|
+
jev: { modelVersion: jevDecision_1.JEV_ROUTE_MODEL_VERSION, reasonCodes: [...taskClasses, `decision_failed:${failureCode || 'unknown'}`], latencyMs: jevLatencyMs },
|
|
203
|
+
decisionError: decisionError || 'current-model decision did not produce a valid JSON route',
|
|
204
|
+
attempts: [],
|
|
205
|
+
finalStatus: 'no_candidate',
|
|
206
|
+
retryBudgetMs: base.retryBudgetMs,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
// Transaction pinning keeps one Build on one deployment. It is execution
|
|
210
|
+
// consistency, not a second routing decision: the pin can only ever hold a
|
|
211
|
+
// deployment the guard already accepted, and a fresh Auto route outside the
|
|
212
|
+
// transaction is decided by JEV alone.
|
|
213
|
+
let primary = validated.primary;
|
|
214
|
+
let alternates = validated.alternates;
|
|
215
|
+
let pinReason;
|
|
216
|
+
const pinned = input.pinnedDeployment;
|
|
217
|
+
if (pinned
|
|
218
|
+
&& !(0, routeEligibility_1.sameDeploymentRef)(pinned, primary)
|
|
219
|
+
&& eligibility.eligible.some(candidate => (0, routeEligibility_1.sameDeploymentRef)(candidate.deployment, pinned))) {
|
|
220
|
+
const previousPrimary = validated.primary;
|
|
221
|
+
primary = { ...pinned };
|
|
222
|
+
pinReason = 'transaction';
|
|
223
|
+
alternates = [previousPrimary, ...validated.alternates]
|
|
224
|
+
.filter(deployment => !(0, routeEligibility_1.sameDeploymentRef)(deployment, primary))
|
|
225
|
+
.filter((deployment, index, list) => list.findIndex(item => (0, routeEligibility_1.sameDeploymentRef)(item, deployment)) === index)
|
|
226
|
+
.slice(0, jevDecision_1.DEFAULT_JEV_MAX_ALTERNATES);
|
|
227
|
+
}
|
|
228
|
+
return buildRouteDecision({
|
|
229
|
+
routeId: base.routeId,
|
|
230
|
+
selection,
|
|
231
|
+
policyVersion: base.policyVersion,
|
|
232
|
+
decisionSource: 'jev',
|
|
233
|
+
catalogSnapshotHash: catalogHash,
|
|
234
|
+
taskClasses,
|
|
235
|
+
excluded: eligibility.excluded,
|
|
236
|
+
decision: { ...validated, primary, alternates },
|
|
237
|
+
jev: {
|
|
238
|
+
modelVersion: validated.modelVersion,
|
|
239
|
+
...(validated.confidence === undefined ? {} : { confidence: validated.confidence }),
|
|
240
|
+
reasonCodes: validated.reasonCodes,
|
|
241
|
+
latencyMs: jevLatencyMs,
|
|
242
|
+
},
|
|
243
|
+
...(pinReason ? { pinReason } : {}),
|
|
244
|
+
attempts: [{ deployment: { ...primary }, kind: 'initial', status: 'planned' }],
|
|
245
|
+
finalStatus: 'resolved',
|
|
246
|
+
retryBudgetMs: base.retryBudgetMs,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=routeDecisionValidator.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RouteEligibilityGuard — Auto Router v2 hard-eligibility stage.
|
|
3
|
+
*
|
|
4
|
+
* This module answers exactly one question: "which candidate deployments may
|
|
5
|
+
* the decision engine even see?". It never answers "which candidate is
|
|
6
|
+
* better". Ranking, scoring, learned preference, cost/speed utility and cache
|
|
7
|
+
* affinity are therefore absent by design; the only decision source downstream
|
|
8
|
+
* of this file is the JEV decision engine.
|
|
9
|
+
*
|
|
10
|
+
* Keep this file free of history: no feedback log, no quality_by_task, no
|
|
11
|
+
* user-rating history, no endpoint statistics. Endpoint health is operational
|
|
12
|
+
* input for the decision engine, not an eligibility rule (an endpoint that is
|
|
13
|
+
* currently failing must stay visible so the system can still probe it).
|
|
14
|
+
*/
|
|
15
|
+
import type { AutoRouteCandidate, AutoScope, DeploymentRef, ModelSelection, RoutePolicy, RouteRequest } from './autoRouter';
|
|
16
|
+
export interface RouteExclusion {
|
|
17
|
+
deployment: DeploymentRef;
|
|
18
|
+
reasons: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface RouteEligibilityRequest {
|
|
21
|
+
selection: ModelSelection;
|
|
22
|
+
policy: RoutePolicy;
|
|
23
|
+
request: RouteRequest;
|
|
24
|
+
candidates: AutoRouteCandidate[];
|
|
25
|
+
}
|
|
26
|
+
export interface RouteEligibilityResult {
|
|
27
|
+
eligible: AutoRouteCandidate[];
|
|
28
|
+
excluded: RouteExclusion[];
|
|
29
|
+
}
|
|
30
|
+
export declare function deploymentKey(deployment: DeploymentRef): string;
|
|
31
|
+
export declare function sameDeploymentRef(left: DeploymentRef, right: DeploymentRef): boolean;
|
|
32
|
+
export declare function inAutoScope(deployment: DeploymentRef, scope: AutoScope): boolean;
|
|
33
|
+
export declare function inAutoSubset(deployment: DeploymentRef, subset: DeploymentRef[] | undefined): boolean;
|
|
34
|
+
export declare function expectedRequestCost(candidate: AutoRouteCandidate, request: RouteRequest): number | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* The single authoritative implementation of Auto hard eligibility.
|
|
37
|
+
*
|
|
38
|
+
* `Agent.autoRouteCandidates()` only projects configuration into candidate
|
|
39
|
+
* facts (enabled flag, capability list, prices, context window, privacy and
|
|
40
|
+
* region declarations, observed endpoint health). Every hard rule that can
|
|
41
|
+
* remove a candidate lives here so there is never a second copy of the rules
|
|
42
|
+
* in the Agent or in the execution controller.
|
|
43
|
+
*/
|
|
44
|
+
export declare function evaluateRouteEligibility(input: RouteEligibilityRequest): RouteEligibilityResult;
|
|
45
|
+
/**
|
|
46
|
+
* A candidate excluded by the Guard may still be used by provider-local
|
|
47
|
+
* recovery when *only* the fallback-only marker removed it. This mirrors the
|
|
48
|
+
* pre-existing behaviour: fallback-only deployments are never primary
|
|
49
|
+
* candidates but may serve as an explicit recovery pool.
|
|
50
|
+
*/
|
|
51
|
+
export declare function excludedOnlyForFallbackPool(exclusion: RouteExclusion | undefined): boolean;
|
|
52
|
+
//# sourceMappingURL=routeEligibility.d.ts.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deploymentKey = deploymentKey;
|
|
4
|
+
exports.sameDeploymentRef = sameDeploymentRef;
|
|
5
|
+
exports.inAutoScope = inAutoScope;
|
|
6
|
+
exports.inAutoSubset = inAutoSubset;
|
|
7
|
+
exports.expectedRequestCost = expectedRequestCost;
|
|
8
|
+
exports.evaluateRouteEligibility = evaluateRouteEligibility;
|
|
9
|
+
exports.excludedOnlyForFallbackPool = excludedOnlyForFallbackPool;
|
|
10
|
+
function deploymentKey(deployment) {
|
|
11
|
+
return `${deployment.providerId}\u0000${deployment.modelId}\u0000${deployment.logicalModelGroupId || ''}`;
|
|
12
|
+
}
|
|
13
|
+
function sameDeploymentRef(left, right) {
|
|
14
|
+
return left.providerId === right.providerId && left.modelId === right.modelId;
|
|
15
|
+
}
|
|
16
|
+
function inAutoScope(deployment, scope) {
|
|
17
|
+
return scope.kind === 'global' || deployment.providerId === scope.providerId;
|
|
18
|
+
}
|
|
19
|
+
function inAutoSubset(deployment, subset) {
|
|
20
|
+
if (!subset?.length)
|
|
21
|
+
return true;
|
|
22
|
+
return subset.some(item => sameDeploymentRef(item, deployment));
|
|
23
|
+
}
|
|
24
|
+
function expectedRequestCost(candidate, request) {
|
|
25
|
+
const input = finiteNumber(candidate.expectedInputCostUsdPerM);
|
|
26
|
+
const output = finiteNumber(candidate.expectedOutputCostUsdPerM);
|
|
27
|
+
if (input === undefined || output === undefined)
|
|
28
|
+
return undefined;
|
|
29
|
+
return input * Math.max(0, request.estimatedInputTokens) / 1_000_000
|
|
30
|
+
+ output * Math.max(0, request.expectedOutputTokens) / 1_000_000;
|
|
31
|
+
}
|
|
32
|
+
function finiteNumber(value) {
|
|
33
|
+
const number = Number(value);
|
|
34
|
+
return Number.isFinite(number) ? number : undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The single authoritative implementation of Auto hard eligibility.
|
|
38
|
+
*
|
|
39
|
+
* `Agent.autoRouteCandidates()` only projects configuration into candidate
|
|
40
|
+
* facts (enabled flag, capability list, prices, context window, privacy and
|
|
41
|
+
* region declarations, observed endpoint health). Every hard rule that can
|
|
42
|
+
* remove a candidate lives here so there is never a second copy of the rules
|
|
43
|
+
* in the Agent or in the execution controller.
|
|
44
|
+
*/
|
|
45
|
+
function evaluateRouteEligibility(input) {
|
|
46
|
+
const { selection, policy, request, candidates } = input;
|
|
47
|
+
const eligible = [];
|
|
48
|
+
const excluded = [];
|
|
49
|
+
if (selection.kind === 'fixed') {
|
|
50
|
+
// Fixed selections bypass Auto eligibility entirely; the caller resolves
|
|
51
|
+
// the deployment directly. Returning an empty guard result keeps this
|
|
52
|
+
// function honest about what it can decide.
|
|
53
|
+
return { eligible, excluded };
|
|
54
|
+
}
|
|
55
|
+
for (const candidate of candidates) {
|
|
56
|
+
const reasons = [];
|
|
57
|
+
if (!candidate.enabled)
|
|
58
|
+
reasons.push('disabled');
|
|
59
|
+
if (candidate.unavailableReason)
|
|
60
|
+
reasons.push(candidate.unavailableReason);
|
|
61
|
+
if (!inAutoScope(candidate.deployment, selection.scope))
|
|
62
|
+
reasons.push('outside_scope');
|
|
63
|
+
if (!inAutoSubset(candidate.deployment, selection.subset))
|
|
64
|
+
reasons.push('outside_subset');
|
|
65
|
+
if (candidate.fallbackOnly)
|
|
66
|
+
reasons.push('fallback_only');
|
|
67
|
+
if (candidate.preview && !policy.allowPreview)
|
|
68
|
+
reasons.push('preview_disallowed');
|
|
69
|
+
if (request.estimatedInputTokens + request.expectedOutputTokens > Math.max(0, candidate.maxContextTokens || 0)) {
|
|
70
|
+
reasons.push('context_too_small');
|
|
71
|
+
}
|
|
72
|
+
if (policy.privacy !== 'default' && !candidate.privacy.includes(policy.privacy))
|
|
73
|
+
reasons.push(`privacy:${policy.privacy}`);
|
|
74
|
+
if (policy.dataRegion) {
|
|
75
|
+
const requiredRegion = policy.dataRegion.toLowerCase();
|
|
76
|
+
const regions = new Set((candidate.dataRegions || []).map(item => String(item).toLowerCase()));
|
|
77
|
+
if (!regions.has(requiredRegion))
|
|
78
|
+
reasons.push(`data_region:${policy.dataRegion}`);
|
|
79
|
+
}
|
|
80
|
+
const supportedParameters = new Set((candidate.supportedProtocolParameters || []).map(item => String(item).toLowerCase()));
|
|
81
|
+
for (const parameter of policy.requiredProtocolParameters || []) {
|
|
82
|
+
const requiredParameter = String(parameter).toLowerCase();
|
|
83
|
+
if (requiredParameter && !supportedParameters.has(requiredParameter))
|
|
84
|
+
reasons.push(`protocol_parameter:${requiredParameter}`);
|
|
85
|
+
}
|
|
86
|
+
const expectedCost = expectedRequestCost(candidate, request);
|
|
87
|
+
if (policy.maxExpectedCostUsd !== undefined && (expectedCost === undefined || expectedCost > policy.maxExpectedCostUsd)) {
|
|
88
|
+
reasons.push(expectedCost === undefined ? 'unknown_cost' : 'budget_exceeded');
|
|
89
|
+
}
|
|
90
|
+
if (reasons.length)
|
|
91
|
+
excluded.push({ deployment: { ...candidate.deployment }, reasons: [...new Set(reasons)] });
|
|
92
|
+
else
|
|
93
|
+
eligible.push(candidate);
|
|
94
|
+
}
|
|
95
|
+
return { eligible, excluded };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* A candidate excluded by the Guard may still be used by provider-local
|
|
99
|
+
* recovery when *only* the fallback-only marker removed it. This mirrors the
|
|
100
|
+
* pre-existing behaviour: fallback-only deployments are never primary
|
|
101
|
+
* candidates but may serve as an explicit recovery pool.
|
|
102
|
+
*/
|
|
103
|
+
function excludedOnlyForFallbackPool(exclusion) {
|
|
104
|
+
return !!exclusion
|
|
105
|
+
&& exclusion.reasons.length > 0
|
|
106
|
+
&& exclusion.reasons.every(reason => reason === 'fallback_only');
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=routeEligibility.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runtimeMemoryBudget = runtimeMemoryBudget;
|
|
4
|
+
const node_os_1 = require("node:os");
|
|
5
|
+
/** Physical RAM policy; never evicts an active run or changes model context. */
|
|
6
|
+
function runtimeMemoryBudget(totalBytes = (0, node_os_1.totalmem)()) {
|
|
7
|
+
const constrained = !Number.isFinite(totalBytes) || totalBytes <= 8 * 1024 ** 3;
|
|
8
|
+
return constrained
|
|
9
|
+
? { maxResidentRuntimes: 2, idleTtlMs: 30_000 }
|
|
10
|
+
: { maxResidentRuntimes: 8, idleTtlMs: 300_000 };
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=runtimeMemoryBudget.js.map
|
|
@@ -12,7 +12,7 @@ export interface ToolPolicyDecision {
|
|
|
12
12
|
settingsVisible: boolean;
|
|
13
13
|
reason?: string;
|
|
14
14
|
}
|
|
15
|
-
export declare const PLAN_COMPUTER_USE_ACTIONS: readonly ["observe", "app_list", "app_observe"];
|
|
15
|
+
export declare const PLAN_COMPUTER_USE_ACTIONS: readonly ["observe", "app_list", "app_observe", "wait_for"];
|
|
16
16
|
export declare const PLAN_BROWSER_USE_ACTIONS: readonly ["observe", "navigate", "wait", "extract"];
|
|
17
17
|
/** 判断一个工具是否可参与并行调度。缺省 false(独占)。
|
|
18
18
|
* 优先看 toolchain registry 推断的 riskLevel('read' 工具天然并发安全,
|
package/dist/core/toolPolicy.js
CHANGED
|
@@ -78,7 +78,7 @@ const PLAN_READ_ONLY_TOOLS = new Set([
|
|
|
78
78
|
'branch_read',
|
|
79
79
|
'question',
|
|
80
80
|
]);
|
|
81
|
-
exports.PLAN_COMPUTER_USE_ACTIONS = ['observe', 'app_list', 'app_observe'];
|
|
81
|
+
exports.PLAN_COMPUTER_USE_ACTIONS = ['observe', 'app_list', 'app_observe', 'wait_for'];
|
|
82
82
|
exports.PLAN_BROWSER_USE_ACTIONS = ['observe', 'navigate', 'wait', 'extract'];
|
|
83
83
|
const PLAN_COMPUTER_USE_ACTION_SET = new Set(exports.PLAN_COMPUTER_USE_ACTIONS);
|
|
84
84
|
const PLAN_BROWSER_USE_ACTION_SET = new Set(exports.PLAN_BROWSER_USE_ACTIONS);
|
|
@@ -3,7 +3,7 @@ import { BrowserUseRequest } from './browserUse';
|
|
|
3
3
|
import { AgentPromptMessage, ConversationContextCompressOptions, ConversationKernelRunOptions, ConversationKernelRunResult, ConversationQueueAction, ConversationQueueActionInput, ConversationQueueMode, ConversationRuntimeState, ConversationStopResult } from './conversationKernel';
|
|
4
4
|
import { ConversationRuntimeTarget, NormalizedConversationTarget } from './conversationTarget';
|
|
5
5
|
import { AgentMode, AgentWorkEvent, ConversationInputEnvelope, GuideReceipt } from './types';
|
|
6
|
-
import type {
|
|
6
|
+
import type { ConversationSnapshot } from './agent';
|
|
7
7
|
export interface UtilityPromptRequest {
|
|
8
8
|
message: string | AgentPromptMessage;
|
|
9
9
|
target: ConversationRuntimeTarget;
|
|
@@ -78,9 +78,16 @@ export type UtilityAgentRequest = {
|
|
|
78
78
|
method: 'snapshot';
|
|
79
79
|
params: {
|
|
80
80
|
target: ConversationRuntimeTarget;
|
|
81
|
+
/**
|
|
82
|
+
* dev-0.6.6: `transcript: 'force'` always returns the full transcript;
|
|
83
|
+
* `transcriptRevision` lets a caller that already holds that exact
|
|
84
|
+
* revision receive a slim snapshot instead of a full re-transfer.
|
|
85
|
+
*/
|
|
81
86
|
options?: {
|
|
82
87
|
window?: number;
|
|
83
88
|
before?: number;
|
|
89
|
+
transcript?: 'auto' | 'force';
|
|
90
|
+
transcriptRevision?: string;
|
|
84
91
|
};
|
|
85
92
|
};
|
|
86
93
|
} | {
|
|
@@ -125,14 +132,6 @@ export type UtilityAgentRequest = {
|
|
|
125
132
|
target: ConversationRuntimeTarget;
|
|
126
133
|
options?: ConversationContextCompressOptions;
|
|
127
134
|
};
|
|
128
|
-
} | {
|
|
129
|
-
id: string;
|
|
130
|
-
method: 'rate_auto_route';
|
|
131
|
-
params: {
|
|
132
|
-
target: ConversationRuntimeTarget;
|
|
133
|
-
score: number;
|
|
134
|
-
routeId?: string;
|
|
135
|
-
};
|
|
136
135
|
} | {
|
|
137
136
|
id: string;
|
|
138
137
|
method: 'set_work_run_expanded';
|
|
@@ -240,7 +239,6 @@ export interface UtilityAgentSnapshotResult {
|
|
|
240
239
|
[key: string]: unknown;
|
|
241
240
|
}
|
|
242
241
|
export type UtilityGuideResult = GuideReceipt;
|
|
243
|
-
export type UtilityAutoRouteRatingResult = AutoRouteRatingResult;
|
|
244
242
|
export type UtilityConversationRewindResult = ConversationSnapshot;
|
|
245
243
|
export {};
|
|
246
244
|
//# sourceMappingURL=utilityAgentProtocol.d.ts.map
|
|
@@ -175,6 +175,8 @@ function createUtilityHostToolHandler(options) {
|
|
|
175
175
|
dryRun: args.dry_run === true || args.dryRun === true,
|
|
176
176
|
captureMaxWidth: Number(args.capture_max_width || args.captureMaxWidth),
|
|
177
177
|
captureMaxHeight: Number(args.capture_max_height || args.captureMaxHeight),
|
|
178
|
+
sparseWaitMs: Number(args.sparse_wait_ms ?? args.sparseWaitMs),
|
|
179
|
+
signal,
|
|
178
180
|
gradientColors: Array.isArray(args.gradient_colors) ? args.gradient_colors : undefined,
|
|
179
181
|
gradientSpeed: Number(args.gradient_speed || 0) || undefined,
|
|
180
182
|
gradientWidth: Number(args.gradient_width || 0) || undefined,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a data URL within `maxBytes`, or `null` when the input is not a
|
|
3
|
+
* decodable image (callers keep their existing path-based fallback then).
|
|
4
|
+
*/
|
|
5
|
+
export declare function downscaleDataUrlToBytes(dataUrl: string, maxBytes: number): string | null;
|
|
6
|
+
//# sourceMappingURL=visualDownscale.d.ts.map
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.downscaleDataUrlToBytes = downscaleDataUrlToBytes;
|
|
37
|
+
const pngjs_1 = require("pngjs");
|
|
38
|
+
const jpeg = __importStar(require("jpeg-js"));
|
|
39
|
+
const conversationVisualBudget_1 = require("./conversationVisualBudget");
|
|
40
|
+
const MAX_SOURCE_PIXELS = 12 * 1024 * 1024;
|
|
41
|
+
const MIN_SCALE = 0.08;
|
|
42
|
+
function decodeDataUrl(dataUrl) {
|
|
43
|
+
const match = /^data:(image\/(?:png|jpe?g));base64,([A-Za-z0-9+/=\r\n]+)$/i.exec(String(dataUrl || ''));
|
|
44
|
+
if (!match)
|
|
45
|
+
return null;
|
|
46
|
+
const mimeType = /^image\/png$/i.test(match[1]) ? 'image/png' : 'image/jpeg';
|
|
47
|
+
let bytes;
|
|
48
|
+
try {
|
|
49
|
+
bytes = Buffer.from(match[2].replace(/\s/g, ''), 'base64');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
if (mimeType === 'image/png') {
|
|
56
|
+
const decoded = pngjs_1.PNG.sync.read(bytes);
|
|
57
|
+
if (!decoded?.width || !decoded?.height)
|
|
58
|
+
return null;
|
|
59
|
+
if (decoded.width * decoded.height > MAX_SOURCE_PIXELS)
|
|
60
|
+
return null;
|
|
61
|
+
return { width: decoded.width, height: decoded.height, data: decoded.data, mimeType };
|
|
62
|
+
}
|
|
63
|
+
const decoded = jpeg.decode(bytes, { useTArray: true, formatAsRGBA: true, maxResolutionInMP: 24, maxMemoryUsageInMB: 192 });
|
|
64
|
+
if (!decoded?.width || !decoded?.height)
|
|
65
|
+
return null;
|
|
66
|
+
if (decoded.width * decoded.height > MAX_SOURCE_PIXELS)
|
|
67
|
+
return null;
|
|
68
|
+
return { width: decoded.width, height: decoded.height, data: decoded.data, mimeType };
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function resample(source, scale) {
|
|
75
|
+
const width = Math.max(1, Math.round(source.width * scale));
|
|
76
|
+
const height = Math.max(1, Math.round(source.height * scale));
|
|
77
|
+
const output = Buffer.alloc(width * height * 4);
|
|
78
|
+
const sourceData = Buffer.isBuffer(source.data) ? source.data : Buffer.from(source.data);
|
|
79
|
+
const xRatio = source.width / width;
|
|
80
|
+
const yRatio = source.height / height;
|
|
81
|
+
for (let y = 0; y < height; y += 1) {
|
|
82
|
+
const sourceY = Math.min(source.height - 1, Math.floor(y * yRatio));
|
|
83
|
+
for (let x = 0; x < width; x += 1) {
|
|
84
|
+
const sourceX = Math.min(source.width - 1, Math.floor(x * xRatio));
|
|
85
|
+
const from = (sourceY * source.width + sourceX) * 4;
|
|
86
|
+
const to = (y * width + x) * 4;
|
|
87
|
+
output[to] = sourceData[from];
|
|
88
|
+
output[to + 1] = sourceData[from + 1];
|
|
89
|
+
output[to + 2] = sourceData[from + 2];
|
|
90
|
+
output[to + 3] = sourceData[from + 3];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { width, height, data: output };
|
|
94
|
+
}
|
|
95
|
+
function encodePng(frame) {
|
|
96
|
+
const png = new pngjs_1.PNG({ width: frame.width, height: frame.height });
|
|
97
|
+
frame.data.copy(png.data);
|
|
98
|
+
return `data:image/png;base64,${pngjs_1.PNG.sync.write(png, { deflateLevel: 6, colorType: 6 }).toString('base64')}`;
|
|
99
|
+
}
|
|
100
|
+
function encodeJpeg(frame, quality) {
|
|
101
|
+
const encoded = jpeg.encode({ data: frame.data, width: frame.width, height: frame.height }, quality);
|
|
102
|
+
return `data:image/jpeg;base64,${Buffer.from(encoded.data).toString('base64')}`;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Returns a data URL within `maxBytes`, or `null` when the input is not a
|
|
106
|
+
* decodable image (callers keep their existing path-based fallback then).
|
|
107
|
+
*/
|
|
108
|
+
function downscaleDataUrlToBytes(dataUrl, maxBytes) {
|
|
109
|
+
// No artificial floor: a severe budget may legitimately ask for a very small
|
|
110
|
+
// image, and the caller decides what is usable.
|
|
111
|
+
const limit = Math.max(1024, Math.floor(Number(maxBytes) || 0));
|
|
112
|
+
if (!limit)
|
|
113
|
+
return null;
|
|
114
|
+
if ((0, conversationVisualBudget_1.visualDataUrlBytes)(dataUrl) <= limit)
|
|
115
|
+
return dataUrl;
|
|
116
|
+
const source = decodeDataUrl(dataUrl);
|
|
117
|
+
if (!source)
|
|
118
|
+
return null;
|
|
119
|
+
// Density scales with the allowed pixel area for this budget, then the result
|
|
120
|
+
// is verified by re-encoding (encoder overhead differs between formats).
|
|
121
|
+
let scale = Math.min(1, Math.sqrt(limit / Math.max(1, (0, conversationVisualBudget_1.visualDataUrlBytes)(dataUrl))));
|
|
122
|
+
for (let attempt = 0; attempt < 4 && scale >= MIN_SCALE; attempt += 1) {
|
|
123
|
+
const frame = resample(source, scale);
|
|
124
|
+
const asPng = encodePng(frame);
|
|
125
|
+
if ((0, conversationVisualBudget_1.visualDataUrlBytes)(asPng) <= limit)
|
|
126
|
+
return asPng;
|
|
127
|
+
const asJpeg = encodeJpeg(frame, attempt === 0 ? 72 : 60);
|
|
128
|
+
if ((0, conversationVisualBudget_1.visualDataUrlBytes)(asJpeg) <= limit)
|
|
129
|
+
return asJpeg;
|
|
130
|
+
scale *= 0.6;
|
|
131
|
+
}
|
|
132
|
+
// Last resort: a very small JPEG still gives the model a usable overview.
|
|
133
|
+
const tiny = resample(source, MIN_SCALE);
|
|
134
|
+
const encoded = encodeJpeg(tiny, 45);
|
|
135
|
+
return (0, conversationVisualBudget_1.visualDataUrlBytes)(encoded) <= limit ? encoded : null;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=visualDownscale.js.map
|