@sema-agent/core 1.451.0 → 2.0.0
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/agents/agent-transcript-tool.d.ts +15 -0
- package/dist/agents/agent-transcript-tool.js +122 -0
- package/dist/agents/cascade.js +5 -8
- package/dist/agents/observer.js +3 -0
- package/dist/agents/repair-loop.d.ts +1 -1
- package/dist/agents/repair-loop.js +1 -1
- package/dist/agents/retain-ledger.d.ts +60 -0
- package/dist/agents/retain-ledger.js +225 -0
- package/dist/agents/roster-store.d.ts +1 -0
- package/dist/agents/roster-store.js +9 -0
- package/dist/agents/send-message-tool.d.ts +41 -0
- package/dist/agents/send-message-tool.js +469 -0
- package/dist/agents/subagent.d.ts +33 -108
- package/dist/agents/subagent.js +40 -813
- package/dist/agents/teacher.d.ts +0 -1
- package/dist/agents/teacher.js +0 -9
- package/dist/bin/sema-tb.js +3 -3
- package/dist/brain/anthropic.js +15 -40
- package/dist/brain/openai.js +15 -40
- package/dist/brain/reasoning.d.ts +1 -1
- package/dist/brain/stream-engine.js +2 -2
- package/dist/brain/stream-shared.d.ts +17 -0
- package/dist/brain/stream-shared.js +50 -0
- package/dist/brain/timeout.d.ts +1 -1
- package/dist/brain/timeout.js +1 -1
- package/dist/config/catalog.js +1 -3
- package/dist/config/defaults.d.ts +6 -0
- package/dist/config/defaults.js +6 -0
- package/dist/core/ask-question.d.ts +1 -1
- package/dist/core/ask-question.js +5 -5
- package/dist/core/background-agent-store.d.ts +2 -0
- package/dist/core/background-agent-store.js +5 -1
- package/dist/core/background-shell.d.ts +1 -1
- package/dist/core/checkpoint-store.d.ts +0 -1
- package/dist/core/exec-gate.d.ts +1 -1
- package/dist/core/exec-output-tail.d.ts +19 -1
- package/dist/core/exec-output-tail.js +44 -5
- package/dist/core/file-snapshot-store.d.ts +1 -1
- package/dist/core/fs-write-gate-policy.d.ts +1 -1
- package/dist/core/git-worktree-env.d.ts +1 -1
- package/dist/core/hooks.d.ts +1 -1
- package/dist/core/image-downsample.d.ts +44 -0
- package/dist/core/image-downsample.js +97 -0
- package/dist/core/lsp-session.d.ts +1 -0
- package/dist/core/lsp-session.js +24 -6
- package/dist/core/lsp.d.ts +11 -1
- package/dist/core/lsp.js +63 -6
- package/dist/core/mailbox-store.d.ts +3 -2
- package/dist/core/mailbox-store.js +19 -4
- package/dist/core/mcp.d.ts +3 -45
- package/dist/core/mcp.js +2 -97
- package/dist/core/media-byte-cap.d.ts +1 -1
- package/dist/core/memory-recall.d.ts +0 -12
- package/dist/core/memory-recall.js +0 -222
- package/dist/core/memory.d.ts +0 -6
- package/dist/core/memory.js +0 -88
- package/dist/core/message-utils.d.ts +1 -1
- package/dist/core/oracle-isolation.d.ts +1 -1
- package/dist/core/remote-env.d.ts +1 -1
- package/dist/core/roles.d.ts +1 -1
- package/dist/core/runner/active-skill-scope.d.ts +1 -1
- package/dist/core/runner/assemble-result.d.ts +0 -2
- package/dist/core/runner/memory-consolidation.d.ts +1 -1
- package/dist/core/runner/prepare-memory.d.ts +16 -0
- package/dist/core/runner/prepare-memory.js +191 -0
- package/dist/core/runner/prepare-task.d.ts +4 -28
- package/dist/core/runner/prepare-task.js +29 -208
- package/dist/core/runner/prompt-suggestions.d.ts +1 -1
- package/dist/core/runner/runtask.d.ts +2 -3
- package/dist/core/runner/runtask.js +337 -529
- package/dist/core/runner/session-rule-policy.d.ts +1 -1
- package/dist/core/runner/synthetic-tools.d.ts +5 -5
- package/dist/core/runner/synthetic-tools.js +4 -4
- package/dist/core/runner/tool-disclosure.d.ts +3 -3
- package/dist/core/runner/tool-disclosure.js +2 -2
- package/dist/core/runner/tool-output-projection.d.ts +6 -0
- package/dist/core/runner/tool-output-projection.js +81 -0
- package/dist/core/runtime.d.ts +1 -1
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/session-reconcile.d.ts +1 -1
- package/dist/core/session-reconcile.js +5 -2
- package/dist/core/session-store.d.ts +2 -1
- package/dist/core/session-store.js +2 -1
- package/dist/core/session.d.ts +0 -1
- package/dist/core/task-notification.d.ts +1 -0
- package/dist/core/task-registry-agent.d.ts +115 -0
- package/dist/core/task-registry-agent.js +1163 -0
- package/dist/core/task-registry-monitor.d.ts +13 -0
- package/dist/core/task-registry-monitor.js +347 -0
- package/dist/core/task-registry-shared.d.ts +294 -0
- package/dist/core/task-registry-shared.js +271 -0
- package/dist/core/task-registry-workflow.d.ts +5 -0
- package/dist/core/task-registry-workflow.js +143 -0
- package/dist/core/task-registry.d.ts +54 -213
- package/dist/core/task-registry.js +190 -1766
- package/dist/core/task-tool-shape.d.ts +32 -0
- package/dist/core/task-tool-shape.js +83 -0
- package/dist/core/tool-policy.d.ts +1 -2
- package/dist/core/tool-result-budget.d.ts +1 -1
- package/dist/core/tool-result-store.d.ts +2 -2
- package/dist/core/tool-result-store.js +3 -3
- package/dist/core/tools.d.ts +7 -3
- package/dist/core/tools.js +3 -8
- package/dist/core/trace.d.ts +2 -2
- package/dist/core/types.d.ts +1 -29
- package/dist/core/workflow-journal-store.d.ts +2 -0
- package/dist/core/workflow-journal-store.js +14 -0
- package/dist/core/workflow-run-store-contract.d.ts +1 -1
- package/dist/core/workflow-run-store-contract.js +19 -19
- package/dist/engine/execution-env/node-execution-env.d.ts +14 -0
- package/dist/engine/execution-env/node-execution-env.js +184 -44
- package/dist/engine/lsp/frame-decoder.d.ts +1 -1
- package/dist/engine/lsp/frame-decoder.js +1 -1
- package/dist/engine/lsp/node-lsp-manager.d.ts +4 -2
- package/dist/engine/lsp/node-lsp-manager.js +22 -5
- package/dist/engine/lsp/stdio-lsp-transport.d.ts +1 -1
- package/dist/engine/lsp/stdio-lsp-transport.js +19 -8
- package/dist/index.d.ts +18 -16
- package/dist/index.js +15 -13
- package/dist/internal/harness-types.d.ts +7 -0
- package/dist/internal/harness-types.js +1 -0
- package/dist/internal/harness.d.ts +1 -7
- package/dist/internal/harness.js +1 -0
- package/dist/orchestration/goal.js +2 -2
- package/dist/orchestration/run-workflow-tool.d.ts +2 -1
- package/dist/orchestration/run-workflow-tool.js +29 -6
- package/dist/orchestration/workflow-types.d.ts +193 -0
- package/dist/orchestration/workflow-types.js +54 -0
- package/dist/orchestration/workflow.d.ts +3 -184
- package/dist/orchestration/workflow.js +87 -64
- package/dist/prompt-assembly/assemble.d.ts +3 -3
- package/dist/prompt-assembly/assemble.js +0 -16
- package/dist/prompt-assembly/explain.d.ts +1 -1
- package/dist/prompt-assembly/tool-catalog.d.ts +1 -1
- package/dist/prompts/default.d.ts +1 -6
- package/dist/prompts/default.js +3 -11
- package/dist/scenarios/env.d.ts +1 -1
- package/dist/scenarios/full-body.d.ts +0 -3
- package/dist/scenarios/full-body.js +0 -2
- package/dist/stores/cc/mailbox-store.js +6 -1
- package/dist/stores/file/background-agent-store.d.ts +2 -15
- package/dist/stores/file/background-agent-store.js +33 -102
- package/dist/stores/file/checkpoint-store.d.ts +1 -14
- package/dist/stores/file/checkpoint-store.js +47 -119
- package/dist/stores/file/file-snapshot-store.d.ts +1 -1
- package/dist/stores/file/mailbox-store.d.ts +1 -1
- package/dist/stores/file/mailbox-store.js +9 -7
- package/dist/stores/file/shared-ledger.d.ts +35 -0
- package/dist/stores/file/shared-ledger.js +94 -0
- package/dist/stores/file/workflow-run-store.d.ts +1 -14
- package/dist/stores/file/workflow-run-store.js +24 -93
- package/dist/tools/fs/bash-readonly-classifier.d.ts +8 -0
- package/dist/tools/fs/bash-readonly-classifier.js +166 -0
- package/dist/tools/fs/encoding.d.ts +5 -0
- package/dist/tools/fs/encoding.js +6 -0
- package/dist/tools/fs/fs-bash.d.ts +31 -0
- package/dist/tools/fs/fs-bash.js +672 -0
- package/dist/tools/fs/fs-pdf.d.ts +22 -0
- package/dist/tools/fs/fs-pdf.js +236 -0
- package/dist/tools/fs/fs-read.d.ts +8 -0
- package/dist/tools/fs/fs-read.js +286 -0
- package/dist/tools/fs/fs-search-tools.d.ts +5 -0
- package/dist/tools/fs/fs-search-tools.js +173 -0
- package/dist/tools/fs/fs-shared.d.ts +55 -0
- package/dist/tools/fs/fs-shared.js +144 -0
- package/dist/tools/fs/fs-write.d.ts +8 -0
- package/dist/tools/fs/fs-write.js +423 -0
- package/dist/tools/fs/index.d.ts +12 -65
- package/dist/tools/fs/index.js +26 -2026
- package/dist/tools/fs/notebook.d.ts +43 -0
- package/dist/tools/fs/notebook.js +141 -0
- package/dist/tools/fs/pdf.d.ts +1 -1
- package/dist/tools/fs/repo-map.d.ts +3 -3
- package/dist/tools/fs/repo-map.js +3 -3
- package/dist/tools/fs/safety.d.ts +1 -1
- package/dist/tools/fs/search.d.ts +1 -1
- package/dist/tools/fs/search.js +141 -12
- package/dist/tools/gitea-issue.js +4 -2
- package/dist/tools/monitor.d.ts +1 -1
- package/dist/tools/monitor.js +12 -8
- package/dist/tools/scheduler-tools.d.ts +1 -1
- package/dist/tools/scheduler-tools.js +16 -16
- package/dist/tools/task-list.js +34 -12
- package/dist/tools/web.d.ts +4 -2
- package/dist/tools/web.js +106 -20
- package/dist/tools/worktree.d.ts +1 -1
- package/dist/tools/worktree.js +14 -14
- package/package.json +1 -1
- package/dist/core/dynamic-recall.d.ts +0 -32
- package/dist/core/dynamic-recall.js +0 -76
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { StringDecoder } from "node:string_decoder";
|
|
1
2
|
export const MAX_EXEC_OUTPUT_BYTES = 8 * 1024 * 1024;
|
|
2
3
|
export class RollingTailBuffer {
|
|
3
4
|
maxBytes;
|
|
4
5
|
chunks = [];
|
|
5
6
|
size = 0;
|
|
6
|
-
|
|
7
|
+
headDropped = 0;
|
|
8
|
+
sourceSkipped = 0;
|
|
7
9
|
constructor(maxBytes = MAX_EXEC_OUTPUT_BYTES) {
|
|
8
10
|
this.maxBytes = maxBytes;
|
|
9
11
|
}
|
|
@@ -18,25 +20,62 @@ export class RollingTailBuffer {
|
|
|
18
20
|
if (over >= head.length) {
|
|
19
21
|
this.chunks.shift();
|
|
20
22
|
this.size -= head.length;
|
|
21
|
-
this.
|
|
23
|
+
this.headDropped += head.length;
|
|
22
24
|
}
|
|
23
25
|
else {
|
|
24
26
|
this.chunks[0] = head.subarray(over);
|
|
25
27
|
this.size -= over;
|
|
26
|
-
this.
|
|
28
|
+
this.headDropped += over;
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
recordSkippedBytes(n) {
|
|
31
33
|
if (n > 0)
|
|
32
|
-
this.
|
|
34
|
+
this.sourceSkipped += n;
|
|
33
35
|
}
|
|
34
36
|
result() {
|
|
35
|
-
return { text: Buffer.concat(this.chunks).toString("utf8"), droppedBytes: this.
|
|
37
|
+
return { text: Buffer.concat(this.chunks).toString("utf8"), droppedBytes: this.headDropped + this.sourceSkipped };
|
|
36
38
|
}
|
|
39
|
+
bytes() {
|
|
40
|
+
return Buffer.concat(this.chunks);
|
|
41
|
+
}
|
|
42
|
+
headDroppedBytes() {
|
|
43
|
+
return this.headDropped;
|
|
44
|
+
}
|
|
45
|
+
sourceSkippedBytes() {
|
|
46
|
+
return this.sourceSkipped;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function newStreamCursorState(tail = new RollingTailBuffer()) {
|
|
50
|
+
return { tail, cursorBytes: 0, acceptedBytes: 0, disclosedSkippedBytes: 0, decoder: new StringDecoder("utf8") };
|
|
51
|
+
}
|
|
52
|
+
export function sliceStreamIncrement(s, terminal) {
|
|
53
|
+
const headDropped = s.tail.headDroppedBytes();
|
|
54
|
+
const sourceSkipped = s.tail.sourceSkippedBytes();
|
|
55
|
+
const newSkipped = Math.max(0, sourceSkipped - s.disclosedSkippedBytes);
|
|
56
|
+
s.disclosedSkippedBytes = sourceSkipped;
|
|
57
|
+
const droppedBeforeCursor = Math.max(0, headDropped - s.cursorBytes) + newSkipped;
|
|
58
|
+
const buf = s.tail.bytes();
|
|
59
|
+
const startInTail = Math.min(Math.max(s.cursorBytes, headDropped) - headDropped, buf.length);
|
|
60
|
+
const incBuf = startInTail <= 0 ? buf : buf.subarray(startInTail);
|
|
61
|
+
if (droppedBeforeCursor > 0)
|
|
62
|
+
s.decoder = new StringDecoder("utf8");
|
|
63
|
+
let inc = s.decoder.write(incBuf);
|
|
64
|
+
if (terminal) {
|
|
65
|
+
inc += s.decoder.end();
|
|
66
|
+
s.decoder = new StringDecoder("utf8");
|
|
67
|
+
}
|
|
68
|
+
s.cursorBytes = s.acceptedBytes;
|
|
69
|
+
return { inc, droppedBeforeCursor };
|
|
37
70
|
}
|
|
38
71
|
export function markTruncated(text, droppedBytes) {
|
|
39
72
|
return droppedBytes > 0
|
|
40
73
|
? `[... ${droppedBytes} earlier byte(s) truncated; showing last ${Buffer.byteLength(text, "utf8")} ...]\n${text}`
|
|
41
74
|
: text;
|
|
42
75
|
}
|
|
76
|
+
export function markLiveSpoolRotations(text, liveRotations) {
|
|
77
|
+
if (liveRotations <= 0)
|
|
78
|
+
return text;
|
|
79
|
+
const note = `[spool reclaimed ${liveRotations} time(s) while the process was still writing; bytes appended during those windows may be missing beyond any counted truncation]`;
|
|
80
|
+
return text === "" ? note : `${text}${text.endsWith("\n") ? "" : "\n"}${note}`;
|
|
81
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExecutionEnv } from "../internal/harness.js";
|
|
1
|
+
import type { ExecutionEnv } from "../internal/harness-types.js";
|
|
2
2
|
export interface FileSnapshotError {
|
|
3
3
|
code: "too_large" | "enumerate_failed" | "read_failed" | "restore_failed" | "not_found" | "aborted";
|
|
4
4
|
message: string;
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DocumentContent, ImageContent, TextContent } from "../internal/llm.js";
|
|
2
|
-
import type { SessionTreeEntry } from "../internal/harness.js";
|
|
2
|
+
import type { SessionTreeEntry } from "../internal/harness-types.js";
|
|
3
3
|
import type { PermissionResult, ResolvedAsk, ToolCallRequest } from "./tool-policy.js";
|
|
4
4
|
export interface Hooks {
|
|
5
5
|
preToolUse?(toolName: string, input: unknown, ctx: HookToolContext): PreToolUseResult | undefined | Promise<PreToolUseResult | undefined>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare const MCP_IMAGE_MAX_BASE64: number;
|
|
2
|
+
export declare const IMAGE_TARGET_RAW_SIZE: number;
|
|
3
|
+
export declare const IMAGE_MAX_WIDTH = 2000;
|
|
4
|
+
export declare const IMAGE_MAX_HEIGHT = 2000;
|
|
5
|
+
export type McpImageResizer = (base64: string, mimeType: string) => Promise<{
|
|
6
|
+
base64: string;
|
|
7
|
+
mimeType: string;
|
|
8
|
+
} | undefined>;
|
|
9
|
+
export interface ImageDimensions {
|
|
10
|
+
originalWidth: number;
|
|
11
|
+
originalHeight: number;
|
|
12
|
+
displayWidth: number;
|
|
13
|
+
displayHeight: number;
|
|
14
|
+
}
|
|
15
|
+
export interface DownsampledImage {
|
|
16
|
+
base64: string;
|
|
17
|
+
mimeType: string;
|
|
18
|
+
dimensions?: ImageDimensions;
|
|
19
|
+
}
|
|
20
|
+
export type ImageDownsampler = (input: Buffer, mimeType: string) => Promise<DownsampledImage | undefined>;
|
|
21
|
+
type SharpFactory = (input: Buffer) => SharpOps;
|
|
22
|
+
interface SharpOps {
|
|
23
|
+
metadata: () => Promise<{
|
|
24
|
+
width?: number;
|
|
25
|
+
height?: number;
|
|
26
|
+
format?: string;
|
|
27
|
+
}>;
|
|
28
|
+
resize: (w: number, h: number, o: {
|
|
29
|
+
fit: "inside";
|
|
30
|
+
withoutEnlargement: boolean;
|
|
31
|
+
}) => SharpOps;
|
|
32
|
+
jpeg: (o: {
|
|
33
|
+
quality: number;
|
|
34
|
+
}) => SharpOps;
|
|
35
|
+
png: (o: {
|
|
36
|
+
compressionLevel: number;
|
|
37
|
+
palette: boolean;
|
|
38
|
+
}) => SharpOps;
|
|
39
|
+
toBuffer: () => Promise<Buffer>;
|
|
40
|
+
}
|
|
41
|
+
export declare function createImageDownsampler(sharp: SharpFactory): ImageDownsampler;
|
|
42
|
+
export declare function sharpImageDownsampler(): Promise<ImageDownsampler | undefined>;
|
|
43
|
+
export declare function sharpImageResizer(): Promise<McpImageResizer | undefined>;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export const MCP_IMAGE_MAX_BASE64 = 5 * 1024 * 1024;
|
|
2
|
+
export const IMAGE_TARGET_RAW_SIZE = (MCP_IMAGE_MAX_BASE64 * 3) / 4;
|
|
3
|
+
export const IMAGE_MAX_WIDTH = 2000;
|
|
4
|
+
export const IMAGE_MAX_HEIGHT = 2000;
|
|
5
|
+
export function createImageDownsampler(sharp) {
|
|
6
|
+
const BOX = { fit: "inside", withoutEnlargement: true };
|
|
7
|
+
return async (input, mimeType) => {
|
|
8
|
+
if (input.length === 0)
|
|
9
|
+
return undefined;
|
|
10
|
+
try {
|
|
11
|
+
const meta = await sharp(input).metadata();
|
|
12
|
+
const fmt = meta.format === "jpg" ? "jpeg" : meta.format;
|
|
13
|
+
const passMime = fmt ? `image/${fmt}` : mimeType;
|
|
14
|
+
const originalSize = input.length;
|
|
15
|
+
if (!meta.width || !meta.height) {
|
|
16
|
+
if (originalSize > IMAGE_TARGET_RAW_SIZE) {
|
|
17
|
+
const buf = await sharp(input).jpeg({ quality: 80 }).toBuffer();
|
|
18
|
+
return { base64: buf.toString("base64"), mimeType: "image/jpeg" };
|
|
19
|
+
}
|
|
20
|
+
return { base64: input.toString("base64"), mimeType: passMime };
|
|
21
|
+
}
|
|
22
|
+
const originalWidth = meta.width;
|
|
23
|
+
const originalHeight = meta.height;
|
|
24
|
+
let width = originalWidth;
|
|
25
|
+
let height = originalHeight;
|
|
26
|
+
const dims = (dw, dh) => ({ originalWidth, originalHeight, displayWidth: dw, displayHeight: dh });
|
|
27
|
+
if (originalSize <= IMAGE_TARGET_RAW_SIZE && width <= IMAGE_MAX_WIDTH && height <= IMAGE_MAX_HEIGHT) {
|
|
28
|
+
return { base64: input.toString("base64"), mimeType: passMime, dimensions: dims(width, height) };
|
|
29
|
+
}
|
|
30
|
+
const needsDimensionResize = width > IMAGE_MAX_WIDTH || height > IMAGE_MAX_HEIGHT;
|
|
31
|
+
const isPng = fmt === "png";
|
|
32
|
+
if (!needsDimensionResize) {
|
|
33
|
+
if (isPng) {
|
|
34
|
+
const png = await sharp(input).png({ compressionLevel: 9, palette: true }).toBuffer();
|
|
35
|
+
if (png.length <= IMAGE_TARGET_RAW_SIZE)
|
|
36
|
+
return { base64: png.toString("base64"), mimeType: "image/png", dimensions: dims(width, height) };
|
|
37
|
+
}
|
|
38
|
+
for (const quality of [80, 60, 40, 20]) {
|
|
39
|
+
const buf = await sharp(input).jpeg({ quality }).toBuffer();
|
|
40
|
+
if (buf.length <= IMAGE_TARGET_RAW_SIZE)
|
|
41
|
+
return { base64: buf.toString("base64"), mimeType: "image/jpeg", dimensions: dims(width, height) };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (width > IMAGE_MAX_WIDTH) {
|
|
45
|
+
height = Math.round((height * IMAGE_MAX_WIDTH) / width);
|
|
46
|
+
width = IMAGE_MAX_WIDTH;
|
|
47
|
+
}
|
|
48
|
+
if (height > IMAGE_MAX_HEIGHT) {
|
|
49
|
+
width = Math.round((width * IMAGE_MAX_HEIGHT) / height);
|
|
50
|
+
height = IMAGE_MAX_HEIGHT;
|
|
51
|
+
}
|
|
52
|
+
const resized = await sharp(input).resize(width, height, BOX).toBuffer();
|
|
53
|
+
if (resized.length <= IMAGE_TARGET_RAW_SIZE) {
|
|
54
|
+
return { base64: resized.toString("base64"), mimeType: passMime, dimensions: dims(width, height) };
|
|
55
|
+
}
|
|
56
|
+
if (isPng) {
|
|
57
|
+
const png = await sharp(input).resize(width, height, BOX).png({ compressionLevel: 9, palette: true }).toBuffer();
|
|
58
|
+
if (png.length <= IMAGE_TARGET_RAW_SIZE)
|
|
59
|
+
return { base64: png.toString("base64"), mimeType: "image/png", dimensions: dims(width, height) };
|
|
60
|
+
}
|
|
61
|
+
for (const quality of [80, 60, 40, 20]) {
|
|
62
|
+
const buf = await sharp(input).resize(width, height, BOX).jpeg({ quality }).toBuffer();
|
|
63
|
+
if (buf.length <= IMAGE_TARGET_RAW_SIZE)
|
|
64
|
+
return { base64: buf.toString("base64"), mimeType: "image/jpeg", dimensions: dims(width, height) };
|
|
65
|
+
}
|
|
66
|
+
const smallerWidth = Math.min(width, 1000);
|
|
67
|
+
const smallerHeight = Math.round((height * smallerWidth) / Math.max(width, 1));
|
|
68
|
+
const last = await sharp(input).resize(smallerWidth, smallerHeight, BOX).jpeg({ quality: 20 }).toBuffer();
|
|
69
|
+
return { base64: last.toString("base64"), mimeType: "image/jpeg", dimensions: dims(smallerWidth, smallerHeight) };
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const SHARP_LIMIT_INPUT_PIXELS = 100_000_000;
|
|
77
|
+
export async function sharpImageDownsampler() {
|
|
78
|
+
try {
|
|
79
|
+
const mod = (await import("sharp"));
|
|
80
|
+
const real = (mod.default ?? mod);
|
|
81
|
+
return createImageDownsampler((input) => real(input, { limitInputPixels: SHARP_LIMIT_INPUT_PIXELS }));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export async function sharpImageResizer() {
|
|
88
|
+
const down = await sharpImageDownsampler();
|
|
89
|
+
if (!down)
|
|
90
|
+
return undefined;
|
|
91
|
+
return async (base64, mimeType) => {
|
|
92
|
+
const out = await down(Buffer.from(base64, "base64"), mimeType).catch(() => undefined);
|
|
93
|
+
if (!out || out.base64.length > MCP_IMAGE_MAX_BASE64)
|
|
94
|
+
return undefined;
|
|
95
|
+
return { base64: out.base64, mimeType: out.mimeType };
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -18,6 +18,7 @@ export declare class TransportLspSession implements LspSession {
|
|
|
18
18
|
request(op: LspOperation, params: LspRequestParams, signal?: AbortSignal): Promise<LspResult>;
|
|
19
19
|
private syncOpenedFiles;
|
|
20
20
|
private syncOne;
|
|
21
|
+
private static classifyUnbranded;
|
|
21
22
|
get closed(): boolean;
|
|
22
23
|
openedFiles(): string[];
|
|
23
24
|
warmOpen(filePaths: string[], signal?: AbortSignal): Promise<void>;
|
package/dist/core/lsp-session.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { uriToPath, buildRequest, callHierarchyMethod, parseResult, pathToUri } from "./lsp-protocol.js";
|
|
2
|
+
import { lspFailureOf } from "./lsp.js";
|
|
2
3
|
export class TransportLspSession {
|
|
3
4
|
transport;
|
|
4
5
|
languageId;
|
|
@@ -37,7 +38,7 @@ export class TransportLspSession {
|
|
|
37
38
|
async request(op, params, signal) {
|
|
38
39
|
const built = buildRequest(op, params);
|
|
39
40
|
if (!built)
|
|
40
|
-
return { kind: "none" };
|
|
41
|
+
return { kind: "none", reason: "unsupported_operation" };
|
|
41
42
|
await this.syncOpenedFiles(params.filePath, signal);
|
|
42
43
|
if (!this.warmupBarrierDone && this.warmup.windowMs > 0) {
|
|
43
44
|
this.warmupBarrierDone = true;
|
|
@@ -49,18 +50,23 @@ export class TransportLspSession {
|
|
|
49
50
|
}
|
|
50
51
|
for (let attempt = 0;; attempt++) {
|
|
51
52
|
let raw;
|
|
53
|
+
let noHierarchyItem = false;
|
|
52
54
|
try {
|
|
53
55
|
raw = await this.transport.request(built.method, built.params, signal);
|
|
54
56
|
if (op === "incomingCalls" || op === "outgoingCalls") {
|
|
55
57
|
const items = Array.isArray(raw) ? raw : raw != null ? [raw] : [];
|
|
56
|
-
|
|
58
|
+
noHierarchyItem = items.length === 0;
|
|
59
|
+
raw = noHierarchyItem ? null : await this.transport.request(callHierarchyMethod(op), { item: items[0] }, signal);
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
catch (e) {
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
const f = lspFailureOf(e) ?? TransportLspSession.classifyUnbranded(signal, this.transport);
|
|
64
|
+
this.log("lsp_op_degraded", { op, file: params.filePath, reason: f.reason, err: e instanceof Error ? e.message : String(e) });
|
|
65
|
+
return { kind: "none", reason: f.reason, ...(f.detail !== undefined ? { detail: f.detail } : {}) };
|
|
62
66
|
}
|
|
63
|
-
const result =
|
|
67
|
+
const result = noHierarchyItem
|
|
68
|
+
? { kind: "none", reason: "no_call_hierarchy_item" }
|
|
69
|
+
: parseResult(op, raw);
|
|
64
70
|
if (result.kind === "none" && this.warmup.windowMs > 0 && Date.now() - this.createdAt < this.warmup.windowMs && !signal?.aborted) {
|
|
65
71
|
await new Promise((r) => setTimeout(r, this.warmup.retryMs));
|
|
66
72
|
continue;
|
|
@@ -92,7 +98,12 @@ export class TransportLspSession {
|
|
|
92
98
|
try {
|
|
93
99
|
text = await this.readText(filePath, signal);
|
|
94
100
|
}
|
|
95
|
-
catch {
|
|
101
|
+
catch (e) {
|
|
102
|
+
this.log("lsp_sync_skipped", {
|
|
103
|
+
file: filePath,
|
|
104
|
+
error: e instanceof Error ? e.message : String(e),
|
|
105
|
+
opened: this.opened.has(filePath),
|
|
106
|
+
});
|
|
96
107
|
return;
|
|
97
108
|
}
|
|
98
109
|
const cur = this.opened.get(filePath);
|
|
@@ -115,6 +126,13 @@ export class TransportLspSession {
|
|
|
115
126
|
this.syncing.set(filePath, job);
|
|
116
127
|
return job.finally(() => this.syncing.delete(filePath));
|
|
117
128
|
}
|
|
129
|
+
static classifyUnbranded(signal, transport) {
|
|
130
|
+
if (signal?.aborted)
|
|
131
|
+
return { reason: "cancelled" };
|
|
132
|
+
if (transport.closed === true)
|
|
133
|
+
return { reason: "server_terminated" };
|
|
134
|
+
return { reason: "server_error" };
|
|
135
|
+
}
|
|
118
136
|
get closed() {
|
|
119
137
|
return this.transport.closed === true;
|
|
120
138
|
}
|
package/dist/core/lsp.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentTool, ExecutionEnv } from "../internal/harness.js";
|
|
1
|
+
import type { AgentTool, ExecutionEnv } from "../internal/harness-types.js";
|
|
2
2
|
export type LspOperation = "goToDefinition" | "findReferences" | "hover" | "documentSymbol" | "workspaceSymbol" | "goToImplementation" | "prepareCallHierarchy" | "incomingCalls" | "outgoingCalls";
|
|
3
3
|
export declare const LSP_OPERATIONS: readonly LspOperation[];
|
|
4
4
|
export interface LspLocation {
|
|
@@ -14,6 +14,7 @@ export interface LspSymbolInfo {
|
|
|
14
14
|
line: number;
|
|
15
15
|
character: number;
|
|
16
16
|
}
|
|
17
|
+
export type LspNoneReason = "unsupported_operation" | "server_error" | "timeout" | "cancelled" | "server_terminated" | "no_call_hierarchy_item";
|
|
17
18
|
export type LspResult = {
|
|
18
19
|
kind: "locations";
|
|
19
20
|
locations: LspLocation[];
|
|
@@ -25,7 +26,15 @@ export type LspResult = {
|
|
|
25
26
|
contents: string;
|
|
26
27
|
} | {
|
|
27
28
|
kind: "none";
|
|
29
|
+
reason?: LspNoneReason;
|
|
30
|
+
detail?: string;
|
|
28
31
|
};
|
|
32
|
+
export declare const LSP_FAILURE_BRAND = "lspFailureReason";
|
|
33
|
+
export declare function brandLspFailure<E extends Error>(e: E, reason: LspNoneReason, detail?: string): E;
|
|
34
|
+
export declare function lspFailureOf(e: unknown): {
|
|
35
|
+
reason: LspNoneReason;
|
|
36
|
+
detail?: string;
|
|
37
|
+
} | undefined;
|
|
29
38
|
export interface LspRequestParams {
|
|
30
39
|
filePath: string;
|
|
31
40
|
line?: number;
|
|
@@ -43,6 +52,7 @@ export interface LspTransport {
|
|
|
43
52
|
readonly closed?: boolean;
|
|
44
53
|
}
|
|
45
54
|
export type LspReadText = (filePath: string, signal?: AbortSignal) => Promise<string>;
|
|
55
|
+
export declare const MAX_LSP_FILE_BYTES: number;
|
|
46
56
|
export interface LspServerManager {
|
|
47
57
|
sessionFor(filePath: string, signal?: AbortSignal, env?: ExecutionEnv): Promise<LspSession | undefined>;
|
|
48
58
|
readonly diagnostics?: import("./lsp-diagnostics.js").LspDiagnosticsRegistry;
|
package/dist/core/lsp.js
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
import { uriToPath } from "./lsp-protocol.js";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
|
-
import { defineTool } from "./tools.js";
|
|
3
|
+
import { defineTool, errorResult } from "./tools.js";
|
|
4
4
|
export const LSP_OPERATIONS = [
|
|
5
5
|
"goToDefinition", "findReferences", "hover", "documentSymbol", "workspaceSymbol",
|
|
6
6
|
"goToImplementation", "prepareCallHierarchy", "incomingCalls", "outgoingCalls",
|
|
7
7
|
];
|
|
8
|
-
|
|
8
|
+
export const LSP_FAILURE_BRAND = "lspFailureReason";
|
|
9
|
+
const LSP_NONE_REASONS = new Set([
|
|
10
|
+
"unsupported_operation", "server_error", "timeout", "cancelled", "server_terminated", "no_call_hierarchy_item",
|
|
11
|
+
]);
|
|
12
|
+
export function brandLspFailure(e, reason, detail) {
|
|
13
|
+
const carrier = e;
|
|
14
|
+
carrier[LSP_FAILURE_BRAND] = reason;
|
|
15
|
+
if (detail !== undefined)
|
|
16
|
+
carrier.lspFailureDetail = detail;
|
|
17
|
+
return e;
|
|
18
|
+
}
|
|
19
|
+
export function lspFailureOf(e) {
|
|
20
|
+
const r = e?.[LSP_FAILURE_BRAND];
|
|
21
|
+
if (typeof r !== "string" || !LSP_NONE_REASONS.has(r))
|
|
22
|
+
return undefined;
|
|
23
|
+
const d = e.lspFailureDetail;
|
|
24
|
+
return { reason: r, ...(typeof d === "string" ? { detail: d } : {}) };
|
|
25
|
+
}
|
|
26
|
+
export const MAX_LSP_FILE_BYTES = 10 * 1000 * 1000;
|
|
27
|
+
function fmt(result, ctx) {
|
|
9
28
|
switch (result.kind) {
|
|
10
29
|
case "hover":
|
|
11
30
|
return result.contents.trim() || "(no hover information)";
|
|
@@ -20,9 +39,30 @@ function fmt(result) {
|
|
|
20
39
|
: `Found ${result.symbols.length} symbol(s):\n` +
|
|
21
40
|
result.symbols.map((s) => `- ${s.name}${s.kind ? ` (${s.kind})` : ""} — ${s.uri}:${s.line}:${s.character}`).join("\n");
|
|
22
41
|
case "none":
|
|
42
|
+
return fmtNone(result, ctx);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function fmtNone(result, ctx) {
|
|
46
|
+
const { operation, filePath } = ctx;
|
|
47
|
+
const detail = result.detail;
|
|
48
|
+
switch (result.reason) {
|
|
49
|
+
case undefined:
|
|
23
50
|
return "No results.";
|
|
51
|
+
case "no_call_hierarchy_item":
|
|
52
|
+
return "No call hierarchy item found at this position";
|
|
53
|
+
case "unsupported_operation":
|
|
54
|
+
return `The language server for "${filePath}" does not support ${operation}${detail ? ` (${detail})` : ""}. Fall back to Grep/Read for this lookup.`;
|
|
55
|
+
case "timeout":
|
|
56
|
+
return `Error performing ${operation}: the language server did not respond in time. This is NOT an empty result — do not conclude the symbol has no ${operation === "findReferences" ? "references" : "results"}; retry or fall back to Grep/Read.`;
|
|
57
|
+
case "server_terminated":
|
|
58
|
+
return `Error performing ${operation}: the language server exited before answering. This is NOT an empty result — retry (a fresh server is spawned on the next call) or fall back to Grep/Read.`;
|
|
59
|
+
case "cancelled":
|
|
60
|
+
return `Error performing ${operation}: the request was cancelled before the language server answered. This is NOT an empty result.`;
|
|
61
|
+
case "server_error":
|
|
62
|
+
return `Error performing ${operation}: ${detail ?? "the language server returned an error"}. This is NOT an empty result — fall back to Grep/Read.`;
|
|
24
63
|
}
|
|
25
64
|
}
|
|
65
|
+
const LSP_ERROR_REASONS = new Set(["server_error", "timeout", "cancelled", "server_terminated"]);
|
|
26
66
|
async function filterIgnored(result, isIgnored) {
|
|
27
67
|
if (!isIgnored)
|
|
28
68
|
return result;
|
|
@@ -80,14 +120,29 @@ export function createLspTool(manager, opts = {}) {
|
|
|
80
120
|
execute: async (args, ctx) => {
|
|
81
121
|
const { operation, filePath, line, character, query, symbol } = args;
|
|
82
122
|
if (POSITION_OPS.has(operation) && (line === undefined || character === undefined)) {
|
|
83
|
-
return `Error (LSP): the "${operation}" operation needs both line and character
|
|
123
|
+
return errorResult(`Error (LSP): the "${operation}" operation needs both line and character.`);
|
|
84
124
|
}
|
|
85
125
|
if (POSITION_OPS.has(operation) && (!Number.isInteger(line) || line < 1 || !Number.isInteger(character) || character < 1)) {
|
|
86
|
-
return `Error (LSP): line and character are 1-based integers (as shown in editors); got line=${line}, character=${character}
|
|
126
|
+
return errorResult(`Error (LSP): line and character are 1-based integers (as shown in editors); got line=${line}, character=${character}.`);
|
|
87
127
|
}
|
|
88
128
|
const wsQuery = query ?? symbol;
|
|
89
129
|
if (operation === "workspaceSymbol" && wsQuery === undefined) {
|
|
90
|
-
return `Error (LSP): the "workspaceSymbol" operation needs a query
|
|
130
|
+
return errorResult(`Error (LSP): the "workspaceSymbol" operation needs a query.`);
|
|
131
|
+
}
|
|
132
|
+
if (opts.env) {
|
|
133
|
+
const isAbsForm = filePath.startsWith("/") || /^[A-Za-z]:[\\/]/.test(filePath);
|
|
134
|
+
const cwd = opts.env.cwd;
|
|
135
|
+
const probe = isAbsForm || !cwd ? filePath : cwd.replace(/[\\/]+$/, "") + (cwd.includes("\\") ? "\\" : "/") + filePath;
|
|
136
|
+
const info = await opts.env.fileInfo(probe, ctx.signal);
|
|
137
|
+
if (!info.ok && info.error.code === "not_found")
|
|
138
|
+
return errorResult(`Error (LSP): File does not exist: ${filePath}`);
|
|
139
|
+
if (!info.ok && info.error.code === "invalid")
|
|
140
|
+
return errorResult(`Error (LSP): Path is not a file: ${filePath}`);
|
|
141
|
+
if (info.ok && info.value.kind === "directory")
|
|
142
|
+
return errorResult(`Error (LSP): Path is not a file: ${filePath}`);
|
|
143
|
+
if (info.ok && info.value.kind === "file" && info.value.size > MAX_LSP_FILE_BYTES) {
|
|
144
|
+
return `File too large for LSP analysis (${Math.ceil(info.value.size / 1e6)}MB exceeds 10MB limit)`;
|
|
145
|
+
}
|
|
91
146
|
}
|
|
92
147
|
const session = await manager.sessionFor(filePath, ctx.signal, opts.env);
|
|
93
148
|
if (!session) {
|
|
@@ -95,7 +150,9 @@ export function createLspTool(manager, opts = {}) {
|
|
|
95
150
|
}
|
|
96
151
|
const seamCharacter = character !== undefined ? character - 1 : undefined;
|
|
97
152
|
const result = await session.request(operation, { filePath, line, character: seamCharacter, symbol: wsQuery }, ctx.signal);
|
|
98
|
-
|
|
153
|
+
const final = toModelBase(await filterIgnored(result, opts.isPathIgnored));
|
|
154
|
+
const text = fmt(final, { operation, filePath });
|
|
155
|
+
return final.kind === "none" && final.reason !== undefined && LSP_ERROR_REASONS.has(final.reason) ? errorResult(text) : text;
|
|
99
156
|
},
|
|
100
157
|
});
|
|
101
158
|
}
|
|
@@ -15,7 +15,7 @@ export interface MailboxStore {
|
|
|
15
15
|
sentAt: number;
|
|
16
16
|
}): Promise<number>;
|
|
17
17
|
claimLease(scope: string, handle: string, owner: string, ttlMs: number, now?: number): Promise<MailboxLease | null>;
|
|
18
|
-
ack(scope: string, handle: string, upToSeq: number): Promise<void>;
|
|
18
|
+
ack(scope: string, handle: string, owner: string, upToSeq: number): Promise<void>;
|
|
19
19
|
releaseLease(scope: string, handle: string, owner: string): Promise<void>;
|
|
20
20
|
peekCount(scope: string, handle: string): Promise<number>;
|
|
21
21
|
drop(scope: string, handle: string): Promise<void>;
|
|
@@ -23,6 +23,7 @@ export interface MailboxStore {
|
|
|
23
23
|
maxAgeMs?: number;
|
|
24
24
|
}): Promise<number>;
|
|
25
25
|
}
|
|
26
|
+
export declare function newestSentAt(messages: readonly MailboxMessage[]): number | undefined;
|
|
26
27
|
export declare class InMemoryMailboxStore implements MailboxStore {
|
|
27
28
|
private boxes;
|
|
28
29
|
private key;
|
|
@@ -33,7 +34,7 @@ export declare class InMemoryMailboxStore implements MailboxStore {
|
|
|
33
34
|
sentAt: number;
|
|
34
35
|
}): Promise<number>;
|
|
35
36
|
claimLease(scope: string, handle: string, owner: string, ttlMs: number, now?: number): Promise<MailboxLease | null>;
|
|
36
|
-
ack(scope: string, handle: string, upToSeq: number): Promise<void>;
|
|
37
|
+
ack(scope: string, handle: string, owner: string, upToSeq: number): Promise<void>;
|
|
37
38
|
releaseLease(scope: string, handle: string, owner: string): Promise<void>;
|
|
38
39
|
peekCount(scope: string, handle: string): Promise<number>;
|
|
39
40
|
drop(scope: string, handle: string): Promise<void>;
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
+
export function newestSentAt(messages) {
|
|
2
|
+
let newest;
|
|
3
|
+
for (const m of messages) {
|
|
4
|
+
if (!Number.isFinite(m.sentAt))
|
|
5
|
+
return undefined;
|
|
6
|
+
if (newest === undefined || m.sentAt > newest)
|
|
7
|
+
newest = m.sentAt;
|
|
8
|
+
}
|
|
9
|
+
return newest;
|
|
10
|
+
}
|
|
1
11
|
export class InMemoryMailboxStore {
|
|
2
12
|
boxes = new Map();
|
|
3
13
|
key(scope, handle) {
|
|
14
|
+
if (handle.includes("\u0000")) {
|
|
15
|
+
throw new Error(`MailboxStore: unsafe handle ${JSON.stringify(handle)} — a NUL byte makes the composite key ambiguous`);
|
|
16
|
+
}
|
|
4
17
|
return `${scope}\u0000${handle.toLowerCase()}`;
|
|
5
18
|
}
|
|
6
19
|
box(scope, handle) {
|
|
@@ -30,12 +43,14 @@ export class InMemoryMailboxStore {
|
|
|
30
43
|
b.lease = { owner, expiresAt: now + ttlMs, maxSeq };
|
|
31
44
|
return { messages: b.messages.map((m) => ({ ...m })), maxSeq };
|
|
32
45
|
}
|
|
33
|
-
async ack(scope, handle, upToSeq) {
|
|
46
|
+
async ack(scope, handle, owner, upToSeq) {
|
|
34
47
|
const b = this.boxes.get(this.key(scope, handle));
|
|
35
48
|
if (!b)
|
|
36
49
|
return;
|
|
50
|
+
if (b.lease === undefined || b.lease.owner !== owner)
|
|
51
|
+
return;
|
|
37
52
|
b.messages = b.messages.filter((m) => m.seq > upToSeq);
|
|
38
|
-
if (b.lease
|
|
53
|
+
if (b.lease.maxSeq <= upToSeq)
|
|
39
54
|
b.lease = undefined;
|
|
40
55
|
}
|
|
41
56
|
async releaseLease(scope, handle, owner) {
|
|
@@ -56,8 +71,8 @@ export class InMemoryMailboxStore {
|
|
|
56
71
|
for (const b of this.boxes.values()) {
|
|
57
72
|
if (b.scope !== scope)
|
|
58
73
|
continue;
|
|
59
|
-
const newest = b.messages
|
|
60
|
-
if (newest !== undefined && newest
|
|
74
|
+
const newest = newestSentAt(b.messages);
|
|
75
|
+
if (newest !== undefined && newest < now - opts.maxAgeMs) {
|
|
61
76
|
b.messages = [];
|
|
62
77
|
b.lease = undefined;
|
|
63
78
|
dropped++;
|
package/dist/core/mcp.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { AgentTool } from "../internal/harness.js";
|
|
1
|
+
import type { AgentTool } from "../internal/harness-types.js";
|
|
2
2
|
import type { ImageContent, TextContent } from "../internal/llm.js";
|
|
3
|
+
import { type McpImageResizer } from "./image-downsample.js";
|
|
3
4
|
import type { McpServerSpec, OnElicit, ToolEffect } from "./types.js";
|
|
4
5
|
export interface McpToolAxis {
|
|
5
6
|
name: string;
|
|
@@ -69,49 +70,7 @@ export declare const MCP_IDLE_TIMEOUT_HTTP_DEFAULT_MS: number;
|
|
|
69
70
|
export declare function mcpIdleTimeoutMs(kind: "stdio" | "http"): number;
|
|
70
71
|
export declare function normalizeMcpName(name: string): string;
|
|
71
72
|
export declare function clampNameSegment(seg: string, max?: number): string;
|
|
72
|
-
export
|
|
73
|
-
export declare const IMAGE_TARGET_RAW_SIZE: number;
|
|
74
|
-
export declare const IMAGE_MAX_WIDTH = 2000;
|
|
75
|
-
export declare const IMAGE_MAX_HEIGHT = 2000;
|
|
76
|
-
export type McpImageResizer = (base64: string, mimeType: string) => Promise<{
|
|
77
|
-
base64: string;
|
|
78
|
-
mimeType: string;
|
|
79
|
-
} | undefined>;
|
|
80
|
-
export interface ImageDimensions {
|
|
81
|
-
originalWidth: number;
|
|
82
|
-
originalHeight: number;
|
|
83
|
-
displayWidth: number;
|
|
84
|
-
displayHeight: number;
|
|
85
|
-
}
|
|
86
|
-
export interface DownsampledImage {
|
|
87
|
-
base64: string;
|
|
88
|
-
mimeType: string;
|
|
89
|
-
dimensions?: ImageDimensions;
|
|
90
|
-
}
|
|
91
|
-
export type ImageDownsampler = (input: Buffer, mimeType: string) => Promise<DownsampledImage | undefined>;
|
|
92
|
-
type SharpFactory = (input: Buffer) => SharpOps;
|
|
93
|
-
interface SharpOps {
|
|
94
|
-
metadata: () => Promise<{
|
|
95
|
-
width?: number;
|
|
96
|
-
height?: number;
|
|
97
|
-
format?: string;
|
|
98
|
-
}>;
|
|
99
|
-
resize: (w: number, h: number, o: {
|
|
100
|
-
fit: "inside";
|
|
101
|
-
withoutEnlargement: boolean;
|
|
102
|
-
}) => SharpOps;
|
|
103
|
-
jpeg: (o: {
|
|
104
|
-
quality: number;
|
|
105
|
-
}) => SharpOps;
|
|
106
|
-
png: (o: {
|
|
107
|
-
compressionLevel: number;
|
|
108
|
-
palette: boolean;
|
|
109
|
-
}) => SharpOps;
|
|
110
|
-
toBuffer: () => Promise<Buffer>;
|
|
111
|
-
}
|
|
112
|
-
export declare function makeImageDownsampler(sharp: SharpFactory): ImageDownsampler;
|
|
113
|
-
export declare function sharpImageDownsampler(): Promise<ImageDownsampler | undefined>;
|
|
114
|
-
export declare function sharpImageResizer(): Promise<McpImageResizer | undefined>;
|
|
73
|
+
export * from "./image-downsample.js";
|
|
115
74
|
export declare function inferCompactSchema(value: unknown, depth?: number): string;
|
|
116
75
|
export declare function resolveMcpHttpHeaders(t: {
|
|
117
76
|
headers?: Record<string, string>;
|
|
@@ -147,4 +106,3 @@ export type McpSchemaNormalizeResult = {
|
|
|
147
106
|
export declare function normalizeMcpToolSchema(schema: unknown): McpSchemaNormalizeResult;
|
|
148
107
|
export declare function mcpToolSchemaProblem(schema: unknown): string | undefined;
|
|
149
108
|
export declare function materializeMcpTools(specs: McpServerSpec[], principal?: string, onElicit?: OnElicit, imageResizer?: McpImageResizer): Promise<MaterializedMcp>;
|
|
150
|
-
export {};
|