@aexol/spectral 0.9.250 → 0.9.254
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.d.ts +2 -1
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +10 -3
- package/dist/generated/zeus/const.d.ts.map +1 -1
- package/dist/generated/zeus/const.js +51 -6
- package/dist/generated/zeus/index.d.ts +278 -36
- package/dist/generated/zeus/index.d.ts.map +1 -1
- package/dist/mcp/mcp-auth-flow.d.ts +4 -0
- package/dist/mcp/mcp-auth-flow.d.ts.map +1 -1
- package/dist/mcp/mcp-auth-flow.js +7 -4
- package/dist/memory/commands/status.d.ts.map +1 -1
- package/dist/memory/commands/status.js +2 -1
- package/dist/memory/hooks/compaction-trigger.d.ts +14 -1
- package/dist/memory/hooks/compaction-trigger.d.ts.map +1 -1
- package/dist/memory/hooks/compaction-trigger.js +31 -4
- package/dist/relay/dispatcher.d.ts.map +1 -1
- package/dist/relay/dispatcher.js +4 -47
- package/dist/relay/routing.d.ts +1 -1
- package/dist/relay/routing.d.ts.map +1 -1
- package/dist/relay/routing.js +9 -0
- package/dist/sdk/ai/utils/image-source.d.ts +26 -11
- package/dist/sdk/ai/utils/image-source.d.ts.map +1 -1
- package/dist/sdk/ai/utils/image-source.js +184 -47
- package/dist/sdk/ai/utils/transient-error.d.ts +4 -0
- package/dist/sdk/ai/utils/transient-error.d.ts.map +1 -1
- package/dist/sdk/ai/utils/transient-error.js +15 -0
- package/dist/sdk/coding-agent/core/compaction/policy.d.ts +2 -0
- package/dist/sdk/coding-agent/core/compaction/policy.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/compaction/policy.js +2 -0
- package/dist/sdk/coding-agent/core/system-prompt.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/system-prompt.js +4 -0
- package/dist/sdk/coding-agent/core/tools/read.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/tools/read.js +14 -7
- package/dist/sdk/coding-agent/index.d.ts +1 -1
- package/dist/sdk/coding-agent/index.d.ts.map +1 -1
- package/dist/sdk/coding-agent/index.js +1 -1
- package/dist/sdk/coding-agent/utils/image-resize.d.ts +25 -11
- package/dist/sdk/coding-agent/utils/image-resize.d.ts.map +1 -1
- package/dist/sdk/coding-agent/utils/image-resize.js +54 -36
- package/dist/sdk/coding-agent/utils/photon.d.ts.map +1 -1
- package/dist/sdk/coding-agent/utils/photon.js +11 -2
- package/dist/server/agent-bridge.d.ts +32 -0
- package/dist/server/agent-bridge.d.ts.map +1 -1
- package/dist/server/agent-bridge.js +59 -9
- package/dist/server/handlers/mcp-auth.d.ts +25 -0
- package/dist/server/handlers/mcp-auth.d.ts.map +1 -0
- package/dist/server/handlers/mcp-auth.js +106 -0
- package/dist/server/handlers/mcp-status.d.ts +8 -2
- package/dist/server/handlers/mcp-status.d.ts.map +1 -1
- package/dist/server/handlers/mcp-status.js +15 -3
- package/dist/server/preset-chain.d.ts +0 -13
- package/dist/server/preset-chain.d.ts.map +1 -1
- package/dist/server/preset-chain.js +0 -25
- package/dist/server/session-stream/memory.d.ts.map +1 -1
- package/dist/server/session-stream/memory.js +8 -1
- package/dist/server/session-stream.d.ts +15 -56
- package/dist/server/session-stream.d.ts.map +1 -1
- package/dist/server/session-stream.js +41 -271
- package/dist/server/wire.d.ts +18 -32
- package/dist/server/wire.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -18,29 +18,40 @@ function encodeCandidate(buffer, mimeType) {
|
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Resize an image to fit within the specified max dimensions and encoded file size.
|
|
21
|
-
* Returns
|
|
22
|
-
*
|
|
23
|
-
* Uses Photon (Rust/WASM) for image processing. If Photon is not available,
|
|
24
|
-
* returns null.
|
|
25
|
-
*
|
|
26
|
-
* Strategy for staying under maxBytes:
|
|
27
|
-
* 1. First resize to maxWidth/maxHeight
|
|
28
|
-
* 2. Try both PNG and JPEG formats, pick the smaller one
|
|
29
|
-
* 3. If still too large, try JPEG with decreasing quality
|
|
30
|
-
* 4. If still too large, progressively reduce dimensions until 1x1
|
|
21
|
+
* Returns a failure reason when no image can be produced (see ImageResizeOutcome).
|
|
31
22
|
*/
|
|
32
|
-
export async function
|
|
23
|
+
export async function resizeImageOutcome(img, options) {
|
|
33
24
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
34
25
|
// URL-only images have no local bytes to resize.
|
|
35
26
|
const inputData = img.data;
|
|
36
27
|
if (!inputData)
|
|
37
|
-
return
|
|
38
|
-
const inputBuffer = Buffer.from(inputData, "base64");
|
|
28
|
+
return { ok: false, reason: "no-data" };
|
|
39
29
|
const inputBase64Size = Buffer.byteLength(inputData, "utf-8");
|
|
30
|
+
// Fast path: already below the encoded-size limit — pass the original bytes
|
|
31
|
+
// through untouched. Photon (Rust/WASM) is only needed when a resize might
|
|
32
|
+
// actually be required, so small images keep working in standalone binaries
|
|
33
|
+
// where the WASM is not shipped.
|
|
34
|
+
if (inputBase64Size < opts.maxBytes) {
|
|
35
|
+
return {
|
|
36
|
+
ok: true,
|
|
37
|
+
image: {
|
|
38
|
+
data: inputData,
|
|
39
|
+
mimeType: img.mimeType ?? "image/png",
|
|
40
|
+
originalWidth: 0,
|
|
41
|
+
originalHeight: 0,
|
|
42
|
+
width: 0,
|
|
43
|
+
height: 0,
|
|
44
|
+
wasResized: false,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
40
48
|
const photon = await loadPhoton();
|
|
41
49
|
if (!photon) {
|
|
42
|
-
return
|
|
50
|
+
return { ok: false, reason: "pipeline-unavailable" };
|
|
43
51
|
}
|
|
52
|
+
// Decode only when a resize might actually be needed: in-limits payloads
|
|
53
|
+
// already returned via the fast path above without paying for this decode.
|
|
54
|
+
const inputBuffer = Buffer.from(inputData, "base64");
|
|
44
55
|
let image;
|
|
45
56
|
try {
|
|
46
57
|
const inputBytes = new Uint8Array(inputBuffer);
|
|
@@ -50,19 +61,8 @@ export async function resizeImage(img, options) {
|
|
|
50
61
|
rawImage.free();
|
|
51
62
|
const originalWidth = image.get_width();
|
|
52
63
|
const originalHeight = image.get_height();
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
if (originalWidth <= opts.maxWidth && originalHeight <= opts.maxHeight && inputBase64Size < opts.maxBytes) {
|
|
56
|
-
return {
|
|
57
|
-
data: inputData,
|
|
58
|
-
mimeType: img.mimeType ?? `image/${format}`,
|
|
59
|
-
originalWidth,
|
|
60
|
-
originalHeight,
|
|
61
|
-
width: originalWidth,
|
|
62
|
-
height: originalHeight,
|
|
63
|
-
wasResized: false,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
64
|
+
// The image is at or above the encoded-size limit (in-limits payloads
|
|
65
|
+
// took the fast path above), so it always needs resizing below maxBytes.
|
|
66
66
|
// Calculate initial dimensions respecting max limits
|
|
67
67
|
let targetWidth = originalWidth;
|
|
68
68
|
let targetHeight = originalHeight;
|
|
@@ -95,13 +95,16 @@ export async function resizeImage(img, options) {
|
|
|
95
95
|
for (const candidate of candidates) {
|
|
96
96
|
if (candidate.encodedSize < opts.maxBytes) {
|
|
97
97
|
return {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
ok: true,
|
|
99
|
+
image: {
|
|
100
|
+
data: candidate.data,
|
|
101
|
+
mimeType: candidate.mimeType,
|
|
102
|
+
originalWidth,
|
|
103
|
+
originalHeight,
|
|
104
|
+
width: currentWidth,
|
|
105
|
+
height: currentHeight,
|
|
106
|
+
wasResized: true,
|
|
107
|
+
},
|
|
105
108
|
};
|
|
106
109
|
}
|
|
107
110
|
}
|
|
@@ -116,10 +119,13 @@ export async function resizeImage(img, options) {
|
|
|
116
119
|
currentWidth = nextWidth;
|
|
117
120
|
currentHeight = nextHeight;
|
|
118
121
|
}
|
|
119
|
-
return
|
|
122
|
+
return { ok: false, reason: "too-large" };
|
|
120
123
|
}
|
|
121
124
|
catch {
|
|
122
|
-
|
|
125
|
+
// Decode/processing error (e.g. a corrupt image): the payload is at or
|
|
126
|
+
// above the size limit and could not be resized, so treat it like a
|
|
127
|
+
// genuine size failure.
|
|
128
|
+
return { ok: false, reason: "too-large" };
|
|
123
129
|
}
|
|
124
130
|
finally {
|
|
125
131
|
if (image) {
|
|
@@ -138,3 +144,15 @@ export function formatDimensionNote(result) {
|
|
|
138
144
|
const scale = result.originalWidth / result.width;
|
|
139
145
|
return `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`;
|
|
140
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Resize an image to fit within the specified max dimensions and encoded file size.
|
|
149
|
+
* Returns null if the image cannot be resized below maxBytes.
|
|
150
|
+
*
|
|
151
|
+
* Backward-compatible wrapper around resizeImageOutcome for callers that only
|
|
152
|
+
* care about the image (or nothing). Prefer resizeImageOutcome when the failure
|
|
153
|
+
* reason must be observable.
|
|
154
|
+
*/
|
|
155
|
+
export async function resizeImage(img, options) {
|
|
156
|
+
const outcome = await resizeImageOutcome(img, options);
|
|
157
|
+
return outcome.ok ? outcome.image : null;
|
|
158
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"photon.d.ts","sourceRoot":"","sources":["../../../../src/sdk/coding-agent/utils/photon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,YAAY,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAuFjF;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,cAAc,4BAA4B,CAAC,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"photon.d.ts","sourceRoot":"","sources":["../../../../src/sdk/coding-agent/utils/photon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,YAAY,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAuFjF;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,cAAc,4BAA4B,CAAC,GAAG,IAAI,CAAC,CAmC9F"}
|
|
@@ -100,9 +100,18 @@ export async function loadPhoton() {
|
|
|
100
100
|
photonModule = await import("@silvia-odwyer/photon-node");
|
|
101
101
|
return photonModule;
|
|
102
102
|
}
|
|
103
|
-
catch {
|
|
103
|
+
catch (error) {
|
|
104
|
+
// Never silent: in standalone binaries the WASM is a companion file
|
|
105
|
+
// next to the executable, and a missing photon_rs_bg.wasm surfaces
|
|
106
|
+
// exactly here. Log the cause so agent logs explain why image
|
|
107
|
+
// processing is unavailable instead of failing without a trace.
|
|
108
|
+
console.warn("[spectral] photon image pipeline unavailable:", error instanceof Error ? error.message : error);
|
|
104
109
|
photonModule = null;
|
|
105
|
-
|
|
110
|
+
// Do not cache the failure: the next loadPhoton() call retries, so a
|
|
111
|
+
// WASM that appears later (installed while the process is running) is
|
|
112
|
+
// picked up without a restart.
|
|
113
|
+
loadPromise = null;
|
|
114
|
+
return null;
|
|
106
115
|
}
|
|
107
116
|
finally {
|
|
108
117
|
restoreReadFileSync();
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
* a single spectral session also work normally (the same AgentSession
|
|
49
49
|
* instance is reused across `prompt()` calls).
|
|
50
50
|
*/
|
|
51
|
+
import type { ImageContent } from "../sdk/ai/types.js";
|
|
51
52
|
import { type AgentSessionEvent, type CompactionResult } from "../sdk/coding-agent/index.js";
|
|
52
53
|
import { type AgentSettings } from "./handlers/agent-settings.js";
|
|
53
54
|
import { fetchAllowedModels as defaultFetchAllowedModels } from "../relay/models-fetch.js";
|
|
@@ -160,6 +161,26 @@ export interface AgentBridgeOptions {
|
|
|
160
161
|
*/
|
|
161
162
|
onCloudAgentsAuthRejected?: (reason: string) => void | Promise<void>;
|
|
162
163
|
}
|
|
164
|
+
/** Get model cost from backend credit rates, falling back to hardcoded pricing table. */
|
|
165
|
+
/**
|
|
166
|
+
* Map a wire `ImageAttachment` onto a spectral `ImageContent` block.
|
|
167
|
+
*
|
|
168
|
+
* A hosted attachment (`url`, optionally `imageId`) is passed through as a
|
|
169
|
+
* reference — the provider fetches it, or `resolveImageForProvider` inlines
|
|
170
|
+
* it at send time when the host is local/private. Only legacy attachments
|
|
171
|
+
* carry inline base64. Exported for tests.
|
|
172
|
+
*
|
|
173
|
+
* Legacy S3 presigned urls persisted in history stop working the moment
|
|
174
|
+
* their (typically 3600s) signature expires — every request would fail with
|
|
175
|
+
* "Failed to fetch media from URL ... parameter=image_url". They are
|
|
176
|
+
* rewritten to the stable hosted ref (`/generated-images/<id>`) so every
|
|
177
|
+
* request re-signs a fresh presigned url instead. Other absolute urls are
|
|
178
|
+
* left untouched.
|
|
179
|
+
*
|
|
180
|
+
* Returns `null` when the attachment has neither `data` nor `url` (nothing
|
|
181
|
+
* sendable) so callers can drop it instead of emitting an empty image block.
|
|
182
|
+
*/
|
|
183
|
+
export declare function toImageContent(image: ImageAttachment): ImageContent | null;
|
|
163
184
|
export declare class AgentBridge {
|
|
164
185
|
private session?;
|
|
165
186
|
private suppressNextAgentEndForGeneration;
|
|
@@ -348,6 +369,17 @@ export declare class AgentBridge {
|
|
|
348
369
|
* default when neither the envelope nor SQLite supply a modelId.
|
|
349
370
|
*/
|
|
350
371
|
getFirstAvailableModelId(): string | undefined;
|
|
372
|
+
/**
|
|
373
|
+
* Return the modelId last successfully applied via `setModel()` — after
|
|
374
|
+
* fallback substitution, if one happened (stale requested modelId →
|
|
375
|
+
* backend default/first available). `undefined` when nothing has been
|
|
376
|
+
* applied yet.
|
|
377
|
+
*
|
|
378
|
+
* Used by `SessionStreamManager.prompt()` to persist the sticky model
|
|
379
|
+
* that is actually in effect, so a session whose persisted model went
|
|
380
|
+
* stale doesn't keep re-persisting the stale id.
|
|
381
|
+
*/
|
|
382
|
+
getLastAppliedModelId(): string | undefined;
|
|
351
383
|
/**
|
|
352
384
|
* Return current session context usage from spectral's built-in estimator.
|
|
353
385
|
* Used after compaction and session start to push updated context-window
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/server/agent-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;
|
|
1
|
+
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/server/agent-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
* a single spectral session also work normally (the same AgentSession
|
|
49
49
|
* instance is reused across `prompt()` calls).
|
|
50
50
|
*/
|
|
51
|
+
import { hostedRefFromPresignedUrl, isS3PresignedUrl } from "../sdk/ai/utils/image-source.js";
|
|
51
52
|
import { AuthStorage, createAgentSession, DefaultResourceLoader, ModelRegistry, SessionManager, SettingsManager, } from "../sdk/coding-agent/index.js";
|
|
52
53
|
import { randomUUID } from "node:crypto";
|
|
53
54
|
import { existsSync, statSync, watch } from "node:fs";
|
|
@@ -140,21 +141,34 @@ function extractTextFromContent(content) {
|
|
|
140
141
|
* A hosted attachment (`url`, optionally `imageId`) is passed through as a
|
|
141
142
|
* reference — the provider fetches it, or `resolveImageForProvider` inlines
|
|
142
143
|
* it at send time when the host is local/private. Only legacy attachments
|
|
143
|
-
* carry inline base64.
|
|
144
|
+
* carry inline base64. Exported for tests.
|
|
145
|
+
*
|
|
146
|
+
* Legacy S3 presigned urls persisted in history stop working the moment
|
|
147
|
+
* their (typically 3600s) signature expires — every request would fail with
|
|
148
|
+
* "Failed to fetch media from URL ... parameter=image_url". They are
|
|
149
|
+
* rewritten to the stable hosted ref (`/generated-images/<id>`) so every
|
|
150
|
+
* request re-signs a fresh presigned url instead. Other absolute urls are
|
|
151
|
+
* left untouched.
|
|
144
152
|
*
|
|
145
153
|
* Returns `null` when the attachment has neither `data` nor `url` (nothing
|
|
146
154
|
* sendable) so callers can drop it instead of emitting an empty image block.
|
|
147
155
|
*/
|
|
148
|
-
function toImageContent(image) {
|
|
156
|
+
export function toImageContent(image) {
|
|
149
157
|
if (!image || typeof image.mimeType !== "string")
|
|
150
158
|
return null;
|
|
151
159
|
if (image.data) {
|
|
152
160
|
return { type: "image", data: image.data, mimeType: image.mimeType };
|
|
153
161
|
}
|
|
154
162
|
if (image.url) {
|
|
163
|
+
let url = image.url;
|
|
164
|
+
if (isS3PresignedUrl(url)) {
|
|
165
|
+
const hostedRef = hostedRefFromPresignedUrl(url);
|
|
166
|
+
if (hostedRef)
|
|
167
|
+
url = hostedRef;
|
|
168
|
+
}
|
|
155
169
|
return {
|
|
156
170
|
type: "image",
|
|
157
|
-
url
|
|
171
|
+
url,
|
|
158
172
|
mimeType: image.mimeType,
|
|
159
173
|
...(image.imageId ? { imageId: image.imageId } : {}),
|
|
160
174
|
};
|
|
@@ -921,6 +935,19 @@ export class AgentBridge {
|
|
|
921
935
|
getFirstAvailableModelId() {
|
|
922
936
|
return this.allowedModels?.[0]?.modelId;
|
|
923
937
|
}
|
|
938
|
+
/**
|
|
939
|
+
* Return the modelId last successfully applied via `setModel()` — after
|
|
940
|
+
* fallback substitution, if one happened (stale requested modelId →
|
|
941
|
+
* backend default/first available). `undefined` when nothing has been
|
|
942
|
+
* applied yet.
|
|
943
|
+
*
|
|
944
|
+
* Used by `SessionStreamManager.prompt()` to persist the sticky model
|
|
945
|
+
* that is actually in effect, so a session whose persisted model went
|
|
946
|
+
* stale doesn't keep re-persisting the stale id.
|
|
947
|
+
*/
|
|
948
|
+
getLastAppliedModelId() {
|
|
949
|
+
return this.lastAppliedModelId;
|
|
950
|
+
}
|
|
924
951
|
/**
|
|
925
952
|
* Return current session context usage from spectral's built-in estimator.
|
|
926
953
|
* Used after compaction and session start to push updated context-window
|
|
@@ -1028,16 +1055,39 @@ export class AgentBridge {
|
|
|
1028
1055
|
}
|
|
1029
1056
|
}
|
|
1030
1057
|
if (!model) {
|
|
1058
|
+
// Whitelist miss after the cache-bypassing refresh: the requested
|
|
1059
|
+
// modelId is no longer active in the backend. Substitute an active
|
|
1060
|
+
// fallback — the backend default (`isDefault`) when present, else the
|
|
1061
|
+
// first available entry (same ordering `getFirstAvailableModelId()`
|
|
1062
|
+
// uses) — so the turn proceeds instead of failing. The client is
|
|
1063
|
+
// notified via a `model_fallback` wire event, and the applied (not
|
|
1064
|
+
// requested) modelId is what `getLastAppliedModelId()` reports for
|
|
1065
|
+
// sticky-model persistence.
|
|
1066
|
+
const fallbackAllowed = this.allowedModels?.find((m) => m.isDefault) ??
|
|
1067
|
+
this.allowedModels?.[0];
|
|
1068
|
+
const fallback = fallbackAllowed
|
|
1069
|
+
? this.modelRegistry
|
|
1070
|
+
.getAvailable()
|
|
1071
|
+
.find((m) => m.id === fallbackAllowed.modelId)
|
|
1072
|
+
: undefined;
|
|
1073
|
+
if (!fallbackAllowed || !fallback) {
|
|
1074
|
+
this.opts.emit({
|
|
1075
|
+
type: "error",
|
|
1076
|
+
message: `Unknown modelId "${modelId}" — not found in spectral model registry${refreshError ? ` after refresh: ${refreshError.message}` : ""}`,
|
|
1077
|
+
});
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1031
1080
|
this.opts.emit({
|
|
1032
|
-
type: "
|
|
1033
|
-
|
|
1081
|
+
type: "model_fallback",
|
|
1082
|
+
requestedModelId: modelId,
|
|
1083
|
+
activeModelId: fallbackAllowed.modelId,
|
|
1034
1084
|
});
|
|
1035
|
-
|
|
1085
|
+
model = fallback;
|
|
1036
1086
|
}
|
|
1037
1087
|
try {
|
|
1038
1088
|
await this.session.setModel(model);
|
|
1039
|
-
this.lastAppliedModelId =
|
|
1040
|
-
const selected = this.allowedModels?.find((m) => m.modelId ===
|
|
1089
|
+
this.lastAppliedModelId = model.id;
|
|
1090
|
+
const selected = this.allowedModels?.find((m) => m.modelId === model.id);
|
|
1041
1091
|
this.activeCreditRates = selected
|
|
1042
1092
|
? {
|
|
1043
1093
|
creditInputPer1M: selected.creditInputPer1M ?? null,
|
|
@@ -1054,7 +1104,7 @@ export class AgentBridge {
|
|
|
1054
1104
|
this.opts.onError?.(e);
|
|
1055
1105
|
this.opts.emit({
|
|
1056
1106
|
type: "error",
|
|
1057
|
-
message: `Failed to switch to modelId "${
|
|
1107
|
+
message: `Failed to switch to modelId "${model.id}": ${humanizeProviderError(e)}`,
|
|
1058
1108
|
});
|
|
1059
1109
|
return false;
|
|
1060
1110
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handler for `POST /api/mcp-auth`.
|
|
3
|
+
*
|
|
4
|
+
* Starts the OAuth flow for a locally configured MCP server managed by
|
|
5
|
+
* `spectral serve`. Runs headless: `startAuth` captures the authorization
|
|
6
|
+
* URL through the provider's `onRedirect` hook (no browser is opened
|
|
7
|
+
* here — the caller renders `authUrl`), and the flow is completed in the
|
|
8
|
+
* background once the local callback server receives the code.
|
|
9
|
+
*
|
|
10
|
+
* Pure handler pattern — no framework, no side effects on the wire. The
|
|
11
|
+
* caller (the relay dispatcher) injects `McpExtensionState`; validation
|
|
12
|
+
* failures throw `BadRequestError` which `relay/rest.ts` maps to 400.
|
|
13
|
+
*/
|
|
14
|
+
import type { McpExtensionState } from "../../mcp/state.js";
|
|
15
|
+
export type McpAuthResult = {
|
|
16
|
+
status: "already-authenticated";
|
|
17
|
+
} | {
|
|
18
|
+
status: "auth_url_ready";
|
|
19
|
+
authUrl: string;
|
|
20
|
+
};
|
|
21
|
+
export interface McpAuthBody {
|
|
22
|
+
name?: unknown;
|
|
23
|
+
}
|
|
24
|
+
export declare function handleStartMcpAuth(state: McpExtensionState | undefined, body: McpAuthBody): Promise<McpAuthResult>;
|
|
25
|
+
//# sourceMappingURL=mcp-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-auth.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/mcp-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAW5D,MAAM,MAAM,aAAa,GACrB;IAAE,MAAM,EAAE,uBAAuB,CAAA;CAAE,GACnC;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AA6BD,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,iBAAiB,GAAG,SAAS,EACpC,IAAI,EAAE,WAAW,GAChB,OAAO,CAAC,aAAa,CAAC,CAyExB"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handler for `POST /api/mcp-auth`.
|
|
3
|
+
*
|
|
4
|
+
* Starts the OAuth flow for a locally configured MCP server managed by
|
|
5
|
+
* `spectral serve`. Runs headless: `startAuth` captures the authorization
|
|
6
|
+
* URL through the provider's `onRedirect` hook (no browser is opened
|
|
7
|
+
* here — the caller renders `authUrl`), and the flow is completed in the
|
|
8
|
+
* background once the local callback server receives the code.
|
|
9
|
+
*
|
|
10
|
+
* Pure handler pattern — no framework, no side effects on the wire. The
|
|
11
|
+
* caller (the relay dispatcher) injects `McpExtensionState`; validation
|
|
12
|
+
* failures throw `BadRequestError` which `relay/rest.ts` maps to 400.
|
|
13
|
+
*/
|
|
14
|
+
import { waitForCallback } from "../../mcp/mcp-callback-server.js";
|
|
15
|
+
import { completeAuth, startAuth, supportsOAuth, } from "../../mcp/mcp-auth-flow.js";
|
|
16
|
+
import { clearOAuthState } from "../../mcp/mcp-auth.js";
|
|
17
|
+
import { reloadMcpConfig } from "../../mcp/init.js";
|
|
18
|
+
import { BadRequestError } from "./errors.js";
|
|
19
|
+
/**
|
|
20
|
+
* Per-server in-flight guard. `startAuth` keys its pending transports and
|
|
21
|
+
* persisted OAuth state by server name, so two concurrent flows for the same
|
|
22
|
+
* server would clobber each other (the headless handler bypasses the
|
|
23
|
+
* `authenticate()` dedupe). Reject the second POST until the first
|
|
24
|
+
* flow's background chain finishes.
|
|
25
|
+
*/
|
|
26
|
+
const inFlightMcpAuths = new Set();
|
|
27
|
+
/**
|
|
28
|
+
* An authorization URL is about to be opened in the user's browser, so it
|
|
29
|
+
* must never be a `javascript:` / `data:` / malformed URL coming from a
|
|
30
|
+
* malicious MCP server's OAuth metadata. Defense in depth: the landing UI
|
|
31
|
+
* applies the same check before `window.open`.
|
|
32
|
+
*/
|
|
33
|
+
function assertSafeAuthorizationUrl(authUrl) {
|
|
34
|
+
let parsed;
|
|
35
|
+
try {
|
|
36
|
+
parsed = new URL(authUrl);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new BadRequestError("Invalid authorization URL");
|
|
40
|
+
}
|
|
41
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
42
|
+
throw new BadRequestError("Invalid authorization URL");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function handleStartMcpAuth(state, body) {
|
|
46
|
+
if (!state) {
|
|
47
|
+
throw new BadRequestError("MCP adapter not loaded");
|
|
48
|
+
}
|
|
49
|
+
const name = body?.name;
|
|
50
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
51
|
+
throw new BadRequestError("name is required");
|
|
52
|
+
}
|
|
53
|
+
const definition = state.config.mcpServers[name];
|
|
54
|
+
if (!definition) {
|
|
55
|
+
throw new BadRequestError(`Unknown MCP server: ${name}`);
|
|
56
|
+
}
|
|
57
|
+
if (!definition.url || !supportsOAuth(definition)) {
|
|
58
|
+
throw new BadRequestError(`MCP server "${name}" does not support OAuth`);
|
|
59
|
+
}
|
|
60
|
+
if (inFlightMcpAuths.has(name)) {
|
|
61
|
+
throw new BadRequestError("OAuth authentication already in progress for this server");
|
|
62
|
+
}
|
|
63
|
+
inFlightMcpAuths.add(name);
|
|
64
|
+
let authorizationUrl;
|
|
65
|
+
let oauthState;
|
|
66
|
+
try {
|
|
67
|
+
({ authorizationUrl, oauthState } = await startAuth(name, definition.url, definition));
|
|
68
|
+
if (authorizationUrl === "") {
|
|
69
|
+
inFlightMcpAuths.delete(name);
|
|
70
|
+
return { status: "already-authenticated" };
|
|
71
|
+
}
|
|
72
|
+
// Fail loudly instead of skipping background completion — a flow without
|
|
73
|
+
// its persisted state could never be completed nor cleanly aborted.
|
|
74
|
+
if (!oauthState) {
|
|
75
|
+
throw new BadRequestError("OAuth state missing after authorization start");
|
|
76
|
+
}
|
|
77
|
+
assertSafeAuthorizationUrl(authorizationUrl);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
// Release the guard so a failed start does not wedge the server.
|
|
81
|
+
inFlightMcpAuths.delete(name);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
// Complete the flow in the background from the local callback server:
|
|
85
|
+
// waitForCallback resolves with the authorization code, completeAuth
|
|
86
|
+
// exchanges it for tokens, and the MCP config is reloaded so the server
|
|
87
|
+
// comes up authenticated. Fire-and-forget — the response already carries
|
|
88
|
+
// the authUrl and auth progress is observable via GET /api/mcp-status,
|
|
89
|
+
// so failures are swallowed. The finally block releases the per-server
|
|
90
|
+
// in-flight guard and clears the now-stale OAuth state (idempotent) on
|
|
91
|
+
// every path.
|
|
92
|
+
waitForCallback(oauthState)
|
|
93
|
+
.then((code) => completeAuth(name, code))
|
|
94
|
+
.then(() => reloadMcpConfig(state))
|
|
95
|
+
.catch(() => { })
|
|
96
|
+
.finally(() => {
|
|
97
|
+
inFlightMcpAuths.delete(name);
|
|
98
|
+
try {
|
|
99
|
+
clearOAuthState(name);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Storage errors must not surface as unhandled rejections.
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return { status: "auth_url_ready", authUrl: authorizationUrl };
|
|
106
|
+
}
|
|
@@ -6,14 +6,20 @@
|
|
|
6
6
|
* The caller (the relay dispatcher) injects `McpExtensionState`.
|
|
7
7
|
*
|
|
8
8
|
* This module intentionally imports ONLY `McpExtensionState` (a pure-type
|
|
9
|
-
* import from `state.ts`)
|
|
10
|
-
*
|
|
9
|
+
* import from `state.ts`) plus the `supportsOAuth` predicate. `supportsOAuth`
|
|
10
|
+
* comes from `mcp-auth-flow.ts` — the same module the `/api/mcp-auth` handler
|
|
11
|
+
* already imports — so this endpoint adds no modules to the dispatcher's
|
|
12
|
+
* graph beyond that handler. What it deliberately avoids is `init.ts` /
|
|
13
|
+
* `config.ts`, whose runtime deps (config loading, consent manager, UI
|
|
14
|
+
* resource handlers) the status snapshot does not need.
|
|
11
15
|
*/
|
|
12
16
|
import type { McpExtensionState } from "../../mcp/state.js";
|
|
13
17
|
export interface McpServerStatus {
|
|
14
18
|
name: string;
|
|
15
19
|
status: "connected" | "disconnected" | "needs-auth" | "failed" | "cached";
|
|
16
20
|
toolCount: number;
|
|
21
|
+
/** Whether the server can run the OAuth flow via `POST /api/mcp-auth`. */
|
|
22
|
+
oauthCapable: boolean;
|
|
17
23
|
/** Epoch-ms of the most recent connection failure, if the server is in a failed state. */
|
|
18
24
|
failedAt?: number;
|
|
19
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-status.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/mcp-status.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mcp-status.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/mcp-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAM5D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,YAAY,EAAE,OAAO,CAAC;IACtB,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,iBAAiB,GACvB,eAAe,EAAE,CAiCnB"}
|
|
@@ -6,14 +6,20 @@
|
|
|
6
6
|
* The caller (the relay dispatcher) injects `McpExtensionState`.
|
|
7
7
|
*
|
|
8
8
|
* This module intentionally imports ONLY `McpExtensionState` (a pure-type
|
|
9
|
-
* import from `state.ts`)
|
|
10
|
-
*
|
|
9
|
+
* import from `state.ts`) plus the `supportsOAuth` predicate. `supportsOAuth`
|
|
10
|
+
* comes from `mcp-auth-flow.ts` — the same module the `/api/mcp-auth` handler
|
|
11
|
+
* already imports — so this endpoint adds no modules to the dispatcher's
|
|
12
|
+
* graph beyond that handler. What it deliberately avoids is `init.ts` /
|
|
13
|
+
* `config.ts`, whose runtime deps (config loading, consent manager, UI
|
|
14
|
+
* resource handlers) the status snapshot does not need.
|
|
11
15
|
*/
|
|
16
|
+
import { supportsOAuth } from "../../mcp/mcp-auth-flow.js";
|
|
12
17
|
/** Maximum age (ms) of a failure entry before we treat it as stale. */
|
|
13
18
|
const FAILURE_BACKOFF_MS = 60_000;
|
|
14
19
|
export function handleListMcpStatus(state) {
|
|
15
20
|
const result = [];
|
|
16
21
|
for (const name of Object.keys(state.config.mcpServers)) {
|
|
22
|
+
const definition = state.config.mcpServers[name];
|
|
17
23
|
const connection = state.manager.getConnection(name);
|
|
18
24
|
const metadata = state.toolMetadata.get(name);
|
|
19
25
|
const toolCount = metadata?.length ?? 0;
|
|
@@ -33,7 +39,13 @@ export function handleListMcpStatus(state) {
|
|
|
33
39
|
else if (metadata !== undefined) {
|
|
34
40
|
status = "cached";
|
|
35
41
|
}
|
|
36
|
-
result.push({
|
|
42
|
+
result.push({
|
|
43
|
+
name,
|
|
44
|
+
status,
|
|
45
|
+
toolCount,
|
|
46
|
+
oauthCapable: supportsOAuth(definition),
|
|
47
|
+
failedAt,
|
|
48
|
+
});
|
|
37
49
|
}
|
|
38
50
|
return result;
|
|
39
51
|
}
|
|
@@ -38,9 +38,6 @@ export interface ResolvedPreset {
|
|
|
38
38
|
followUpMessages: unknown;
|
|
39
39
|
scope?: "TEAM" | "PROJECT" | null;
|
|
40
40
|
projectId?: string | null;
|
|
41
|
-
loopEnabled?: boolean | null;
|
|
42
|
-
maxIterations?: number | null;
|
|
43
|
-
goal?: string | null;
|
|
44
41
|
intervalMinutes?: number | null;
|
|
45
42
|
}
|
|
46
43
|
/** Why an expansion did not happen. */
|
|
@@ -138,14 +135,4 @@ export interface PresetExpansionOutcome {
|
|
|
138
135
|
* the injected `enqueue`/`run` deps propagate to the caller.
|
|
139
136
|
*/
|
|
140
137
|
export declare function expandIfPresetReference(input: ExpandPresetReferenceInput): Promise<PresetExpansionOutcome>;
|
|
141
|
-
/**
|
|
142
|
-
* Expand a prompt that is about to be REPLAYED (autonomous loop arming).
|
|
143
|
-
* Returns the input unchanged when it is not a reference or the resolver
|
|
144
|
-
* fails, so loop arming never blocks on or loses the original prompt. Does
|
|
145
|
-
* NOT touch the chain budget: expansion happens once at arming time, not on
|
|
146
|
-
* every iteration. INJECT follow-ups are folded into the replayed prompt
|
|
147
|
-
* (same composition as the queue-expansion path); QUEUE follow-ups are not
|
|
148
|
-
* enqueued here — loop arming only rewrites the replayed prompt.
|
|
149
|
-
*/
|
|
150
|
-
export declare function expandPresetPrompt(prompt: string, resolve: (name: string) => Promise<ResolvedPreset | null>): Promise<string>;
|
|
151
138
|
//# sourceMappingURL=preset-chain.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset-chain.d.ts","sourceRoot":"","sources":["../../src/server/preset-chain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC;8CAC8C;AAC9C,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAKxC,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,
|
|
1
|
+
{"version":3,"file":"preset-chain.d.ts","sourceRoot":"","sources":["../../src/server/preset-chain.ts"],"names":[],"mappings":"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"}
|
|
@@ -223,28 +223,3 @@ export async function expandIfPresetReference(input) {
|
|
|
223
223
|
budgetReset: false,
|
|
224
224
|
};
|
|
225
225
|
}
|
|
226
|
-
/**
|
|
227
|
-
* Expand a prompt that is about to be REPLAYED (autonomous loop arming).
|
|
228
|
-
* Returns the input unchanged when it is not a reference or the resolver
|
|
229
|
-
* fails, so loop arming never blocks on or loses the original prompt. Does
|
|
230
|
-
* NOT touch the chain budget: expansion happens once at arming time, not on
|
|
231
|
-
* every iteration. INJECT follow-ups are folded into the replayed prompt
|
|
232
|
-
* (same composition as the queue-expansion path); QUEUE follow-ups are not
|
|
233
|
-
* enqueued here — loop arming only rewrites the replayed prompt.
|
|
234
|
-
*/
|
|
235
|
-
export async function expandPresetPrompt(prompt, resolve) {
|
|
236
|
-
const reference = parsePresetReference(prompt);
|
|
237
|
-
if (!reference)
|
|
238
|
-
return prompt;
|
|
239
|
-
const preset = await resolve(reference.name);
|
|
240
|
-
if (!preset ||
|
|
241
|
-
typeof preset.prompt !== "string" ||
|
|
242
|
-
!preset.prompt.trim()) {
|
|
243
|
-
return prompt;
|
|
244
|
-
}
|
|
245
|
-
const followUps = extractFollowUpContents(preset.followUpMessages);
|
|
246
|
-
const injectContents = followUps
|
|
247
|
-
.filter((f) => f.mode === "INJECT")
|
|
248
|
-
.map((f) => f.content);
|
|
249
|
-
return composePresetRunPrompt(preset.prompt, injectContents, reference.remainder);
|
|
250
|
-
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/server/session-stream/memory.ts"],"names":[],"mappings":"AAMA,OAAO,
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/server/session-stream/memory.ts"],"names":[],"mappings":"AAMA,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,kDAAkD,CAAC;AAG1D,OAAO,EAAc,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEjE,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,GACjB,IAAI,CAmCN;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAMlE;AAED,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;AAEvE,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC;IACjF,UAAU,EAAE,wBAAwB,CAAC;CACtC;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,CAAC;IACtE,QAAQ,EAAE;QACR,QAAQ,EAAE,OAAO,CAAC;QAClB,UAAU,EAAE,OAAO,CAAC;QACpB,UAAU,EAAE,OAAO,CAAC;QACpB,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,YAAY,EAAE;QACZ,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;IACF,UAAU,EAAE;QACV,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,QAAQ,EAAE;QACR,2BAA2B,EAAE,MAAM,CAAC;QACpC,yBAAyB,EAAE,MAAM,CAAC;QAClC,qBAAqB,EAAE,MAAM,CAAC;KAC/B,CAAC;IACF,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,gBAAgB,GACvB,wBAAwB,CAa1B;AAED,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,MAAM,GAAG,IAAI,CAQf;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,mBAAmB,EAC3B,MAAM,EAAE,aAAa,GAAG,SAAS,GAChC,IAAI,CAwBN;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,WAAW,GACjB,iBAAiB,CAAC,WAAW,CAAC,GAAG,IAAI,CAoBvC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAiCpE"}
|