negotium 0.2.9 → 0.2.11
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-helpers.js +336 -92
- package/dist/agent-helpers.js.map +8 -6
- package/dist/background-bash.js +2 -1
- package/dist/background-bash.js.map +3 -3
- package/dist/browser-runtime.js +2 -1
- package/dist/browser-runtime.js.map +3 -3
- package/dist/{chunk-knv045mn.js → chunk-dcfmsrnb.js} +2 -1
- package/dist/{chunk-knv045mn.js.map → chunk-dcfmsrnb.js.map} +3 -3
- package/dist/hosted-agent.js +3 -2
- package/dist/hosted-agent.js.map +4 -4
- package/dist/main.js +505 -252
- package/dist/main.js.map +9 -7
- package/dist/mcp-factories.js +4 -3
- package/dist/mcp-factories.js.map +4 -4
- package/dist/prompts.js +2 -1
- package/dist/prompts.js.map +3 -3
- package/dist/query-runtime.js +2 -1
- package/dist/query-runtime.js.map +3 -3
- package/dist/registry.js +3 -3
- package/dist/registry.js.map +2 -2
- package/dist/rollout.js +1 -1
- package/dist/runtime/src/agents/mcp-tools/inline-assets.ts +193 -0
- package/dist/runtime/src/agents/mcp-tools/publish-html.ts +183 -0
- package/dist/runtime/src/agents/public-helpers.ts +4 -0
- package/dist/runtime/src/mcp-runtime-host.ts +1 -0
- package/dist/runtime/src/platform/config.ts +12 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +2 -1
- package/dist/runtime-helpers.js.map +3 -3
- package/dist/types/packages/core/src/agents/mcp-tools/inline-assets.d.ts +24 -0
- package/dist/types/packages/core/src/agents/mcp-tools/publish-html.d.ts +18 -0
- package/dist/types/packages/core/src/agents/public-helpers.d.ts +1 -0
- package/dist/types/packages/core/src/platform/config.d.ts +7 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js +2 -1
- package/dist/vault.js.map +3 -3
- package/package.json +1 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline local file references in an HTML document as data URIs.
|
|
3
|
+
*
|
|
4
|
+
* A published snippet is a single file with no asset directory, so a
|
|
5
|
+
* generated document that points at `./chart.png` would render a broken
|
|
6
|
+
* image. Rewriting those references into `data:` URIs keeps the snippet
|
|
7
|
+
* self-contained without giving the store a second lifecycle to manage.
|
|
8
|
+
*
|
|
9
|
+
* Only references that resolve to an existing local file are touched.
|
|
10
|
+
* Remote URLs, `data:`/`blob:` URIs, protocol-relative URLs and fragments
|
|
11
|
+
* are left exactly as written.
|
|
12
|
+
*
|
|
13
|
+
* The scanner is regex-based rather than a full HTML parse. That is a
|
|
14
|
+
* deliberate tradeoff for machine-generated documents: it handles the
|
|
15
|
+
* attribute shapes models actually emit, and anything it fails to match is
|
|
16
|
+
* simply left alone rather than corrupted.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
19
|
+
import { extname, isAbsolute, resolve } from "node:path";
|
|
20
|
+
|
|
21
|
+
const MIME_BY_EXT: Record<string, string> = {
|
|
22
|
+
".apng": "image/apng",
|
|
23
|
+
".avif": "image/avif",
|
|
24
|
+
".bmp": "image/bmp",
|
|
25
|
+
".css": "text/css",
|
|
26
|
+
".gif": "image/gif",
|
|
27
|
+
".ico": "image/x-icon",
|
|
28
|
+
".jpeg": "image/jpeg",
|
|
29
|
+
".jpg": "image/jpeg",
|
|
30
|
+
".mp3": "audio/mpeg",
|
|
31
|
+
".mp4": "video/mp4",
|
|
32
|
+
".otf": "font/otf",
|
|
33
|
+
".png": "image/png",
|
|
34
|
+
".svg": "image/svg+xml",
|
|
35
|
+
".ttf": "font/ttf",
|
|
36
|
+
".wav": "audio/wav",
|
|
37
|
+
".webm": "video/webm",
|
|
38
|
+
".webp": "image/webp",
|
|
39
|
+
".woff": "font/woff",
|
|
40
|
+
".woff2": "font/woff2",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export interface InlineOptions {
|
|
44
|
+
/** Directory that relative references resolve against. */
|
|
45
|
+
baseDir: string;
|
|
46
|
+
/** Skip any single asset larger than this. */
|
|
47
|
+
maxAssetBytes: number;
|
|
48
|
+
/** Stop inlining once the document would exceed this. */
|
|
49
|
+
maxTotalBytes: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface InlineReport {
|
|
53
|
+
html: string;
|
|
54
|
+
inlined: { ref: string; bytes: number }[];
|
|
55
|
+
/** Looked like a local file but nothing was there — likely a broken image. */
|
|
56
|
+
unresolved: string[];
|
|
57
|
+
/** Existed but was left as-is; the document would have grown too large. */
|
|
58
|
+
skipped: { ref: string; bytes: number; reason: "asset-too-large" | "document-too-large" }[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Remote URL, data/blob URI, protocol-relative URL, or bare fragment. */
|
|
62
|
+
function isNonLocal(ref: string): boolean {
|
|
63
|
+
return /^(?:[a-z][a-z0-9+.-]*:|\/\/|#)/i.test(ref.trim());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** `./chart.png?v=2#frag` → `./chart.png` */
|
|
67
|
+
function stripSuffix(ref: string): string {
|
|
68
|
+
return ref.replace(/[?#].*$/, "");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function inlineLocalAssets(html: string, options: InlineOptions): InlineReport {
|
|
72
|
+
const { baseDir, maxAssetBytes, maxTotalBytes } = options;
|
|
73
|
+
|
|
74
|
+
const inlined: InlineReport["inlined"] = [];
|
|
75
|
+
const unresolved: string[] = [];
|
|
76
|
+
const skipped: InlineReport["skipped"] = [];
|
|
77
|
+
|
|
78
|
+
// Same file referenced twice should only be read (and counted) once.
|
|
79
|
+
const cache = new Map<string, string | null>();
|
|
80
|
+
let budget = maxTotalBytes - Buffer.byteLength(html, "utf-8");
|
|
81
|
+
|
|
82
|
+
/** Returns the data URI for a reference, or null to leave it untouched. */
|
|
83
|
+
function toDataUri(rawRef: string): string | null {
|
|
84
|
+
const ref = rawRef.trim();
|
|
85
|
+
if (!ref || isNonLocal(ref)) return null;
|
|
86
|
+
|
|
87
|
+
const cached = cache.get(ref);
|
|
88
|
+
if (cached !== undefined) return cached;
|
|
89
|
+
|
|
90
|
+
const relPath = stripSuffix(ref);
|
|
91
|
+
if (!relPath) return null;
|
|
92
|
+
|
|
93
|
+
const abs = isAbsolute(relPath) ? relPath : resolve(baseDir, relPath);
|
|
94
|
+
|
|
95
|
+
let bytes: number;
|
|
96
|
+
try {
|
|
97
|
+
if (!existsSync(abs) || !statSync(abs).isFile()) {
|
|
98
|
+
unresolved.push(ref);
|
|
99
|
+
cache.set(ref, null);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
bytes = statSync(abs).size;
|
|
103
|
+
} catch {
|
|
104
|
+
unresolved.push(ref);
|
|
105
|
+
cache.set(ref, null);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (bytes > maxAssetBytes) {
|
|
110
|
+
skipped.push({ ref, bytes, reason: "asset-too-large" });
|
|
111
|
+
cache.set(ref, null);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// base64 costs ~4/3 of the raw bytes; check against the remaining budget
|
|
116
|
+
// before reading so an oversized asset never lands in memory twice.
|
|
117
|
+
const encodedCost = Math.ceil(bytes / 3) * 4;
|
|
118
|
+
if (encodedCost > budget) {
|
|
119
|
+
skipped.push({ ref, bytes, reason: "document-too-large" });
|
|
120
|
+
cache.set(ref, null);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let uri: string;
|
|
125
|
+
try {
|
|
126
|
+
const mime = MIME_BY_EXT[extname(abs).toLowerCase()] ?? "application/octet-stream";
|
|
127
|
+
uri = `data:${mime};base64,${readFileSync(abs).toString("base64")}`;
|
|
128
|
+
} catch {
|
|
129
|
+
unresolved.push(ref);
|
|
130
|
+
cache.set(ref, null);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
budget -= uri.length;
|
|
135
|
+
inlined.push({ ref, bytes });
|
|
136
|
+
cache.set(ref, uri);
|
|
137
|
+
return uri;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** `a.png 1x, b.png 2x` — rewrite each candidate, keep its descriptor. */
|
|
141
|
+
function rewriteSrcset(value: string): string {
|
|
142
|
+
return value
|
|
143
|
+
.split(",")
|
|
144
|
+
.map((candidate) => {
|
|
145
|
+
const trimmed = candidate.trim();
|
|
146
|
+
if (!trimmed) return candidate;
|
|
147
|
+
const [url, ...descriptor] = trimmed.split(/\s+/);
|
|
148
|
+
const uri = url ? toDataUri(url) : null;
|
|
149
|
+
if (!uri) return candidate;
|
|
150
|
+
return [uri, ...descriptor].join(" ");
|
|
151
|
+
})
|
|
152
|
+
.join(", ");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let out = html;
|
|
156
|
+
|
|
157
|
+
// src="..." on img/script/source/video/audio/iframe, and poster="..."
|
|
158
|
+
out = out.replace(
|
|
159
|
+
/(\b(?:src|poster)\s*=\s*)(["'])([^"']*)\2/gi,
|
|
160
|
+
(match, prefix: string, quote: string, ref: string) => {
|
|
161
|
+
const uri = toDataUri(ref);
|
|
162
|
+
return uri ? `${prefix}${quote}${uri}${quote}` : match;
|
|
163
|
+
},
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// srcset="a.png 1x, b.png 2x"
|
|
167
|
+
out = out.replace(
|
|
168
|
+
/(\bsrcset\s*=\s*)(["'])([^"']*)\2/gi,
|
|
169
|
+
(match, prefix: string, quote: string, value: string) => {
|
|
170
|
+
const rewritten = rewriteSrcset(value);
|
|
171
|
+
return rewritten === value ? match : `${prefix}${quote}${rewritten}${quote}`;
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// <link href="style.css"> / icons — but never <a href>.
|
|
176
|
+
out = out.replace(/<link\b[^>]*>/gi, (tag) =>
|
|
177
|
+
tag.replace(
|
|
178
|
+
/(\bhref\s*=\s*)(["'])([^"']*)\2/i,
|
|
179
|
+
(match, prefix: string, quote: string, ref: string) => {
|
|
180
|
+
const uri = toDataUri(ref);
|
|
181
|
+
return uri ? `${prefix}${quote}${uri}${quote}` : match;
|
|
182
|
+
},
|
|
183
|
+
),
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// CSS url(...) in <style> blocks and style="" attributes.
|
|
187
|
+
out = out.replace(/url\(\s*(["']?)([^"')]+)\1\s*\)/gi, (match, quote: string, ref: string) => {
|
|
188
|
+
const uri = toDataUri(ref);
|
|
189
|
+
return uri ? `url(${quote}${uri}${quote})` : match;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return { html: out, inlined, unresolved, skipped };
|
|
193
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `publish_html` — hand the user a public link to a rendered HTML document.
|
|
3
|
+
*
|
|
4
|
+
* This sits beside `show_html`: both exist to show rendered HTML rather than
|
|
5
|
+
* raw markup, so both are gated by the same `visualTools` capability. They
|
|
6
|
+
* differ only in delivery. `show_html` paints a sandboxed in-app panel, which
|
|
7
|
+
* blocks network requests and cannot be opened outside the app; publishing
|
|
8
|
+
* returns an ordinary URL that survives the turn and can be shared.
|
|
9
|
+
*
|
|
10
|
+
* Requires a snippet backend (`NEGOTIUM_SNIPPETS_API_URL`). The tool is left
|
|
11
|
+
* out of the catalog entirely when none is configured, so a deployment
|
|
12
|
+
* without one never advertises a link it cannot mint.
|
|
13
|
+
*/
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { errorResult, type SharedMcpTool, textResult } from "#agents/mcp-tools/common";
|
|
16
|
+
import { inlineLocalAssets } from "#agents/mcp-tools/inline-assets";
|
|
17
|
+
import { SNIPPETS_API_URL } from "#platform/config";
|
|
18
|
+
|
|
19
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
20
|
+
/** Keep in step with the snippet service's own body limit. */
|
|
21
|
+
export const MAX_PUBLISHED_BYTES = 10 * 1024 * 1024;
|
|
22
|
+
export const MAX_PUBLISHED_ASSET_BYTES = 5 * 1024 * 1024;
|
|
23
|
+
|
|
24
|
+
export interface PublishHtmlContext {
|
|
25
|
+
/** Directory that relative asset paths resolve against (the agent's cwd). */
|
|
26
|
+
cwd?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type SnippetResponse = {
|
|
30
|
+
ok: boolean;
|
|
31
|
+
url?: string;
|
|
32
|
+
snippetId?: string;
|
|
33
|
+
expiresAt?: string;
|
|
34
|
+
error?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export function formatPublishBytes(bytes: number): string {
|
|
38
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
39
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)}KB`;
|
|
40
|
+
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Accept a full snippet URL or a bare id, return the id. */
|
|
44
|
+
export function parseSnippetId(input: string): string | null {
|
|
45
|
+
const trimmed = input.trim();
|
|
46
|
+
const fromUrl = trimmed.match(/\/snippets\/([A-Za-z0-9-]+)/)?.[1];
|
|
47
|
+
const id = fromUrl ?? trimmed;
|
|
48
|
+
return /^[A-Za-z0-9-]+$/.test(id) ? id : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function snippetRequest(
|
|
52
|
+
baseUrl: string,
|
|
53
|
+
path: string,
|
|
54
|
+
init: RequestInit = {},
|
|
55
|
+
): Promise<SnippetResponse> {
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch(`${baseUrl}${path}`, {
|
|
58
|
+
...init,
|
|
59
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
60
|
+
});
|
|
61
|
+
const body = (await response.json().catch(() => null)) as SnippetResponse | null;
|
|
62
|
+
if (body) return body;
|
|
63
|
+
return { ok: false, error: `snippet service returned ${response.status}` };
|
|
64
|
+
} catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : "snippet service unreachable";
|
|
66
|
+
return { ok: false, error: message };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Build the publish tools, or `[]` when no snippet backend is configured.
|
|
72
|
+
* `ctx` may be a getter so a long-lived server picks up the current turn's cwd.
|
|
73
|
+
*/
|
|
74
|
+
export function createPublishHtmlToolDefinitions(
|
|
75
|
+
ctx: PublishHtmlContext | (() => PublishHtmlContext),
|
|
76
|
+
options: { apiUrl?: string } = {},
|
|
77
|
+
): SharedMcpTool[] {
|
|
78
|
+
const apiUrl = (options.apiUrl ?? SNIPPETS_API_URL)?.replace(/\/+$/, "");
|
|
79
|
+
if (!apiUrl) return [];
|
|
80
|
+
const getCtx = typeof ctx === "function" ? ctx : () => ctx;
|
|
81
|
+
|
|
82
|
+
return [
|
|
83
|
+
{
|
|
84
|
+
name: "publish_html",
|
|
85
|
+
description: [
|
|
86
|
+
"Publish an HTML document and return a public, shareable https link that",
|
|
87
|
+
"stays live for 72 hours. Use this when the user should be able to open,",
|
|
88
|
+
"keep, or send the result — a report, dashboard, or styled table — rather",
|
|
89
|
+
"than only glance at it in the side panel.",
|
|
90
|
+
"Pass a complete standalone document and inline the CSS and JS.",
|
|
91
|
+
"For charts and diagrams prefer inline <svg>: it stays sharp at any zoom",
|
|
92
|
+
"and is far smaller than an image file.",
|
|
93
|
+
"Images may be remote URLs, data: URIs, or paths to local files you",
|
|
94
|
+
"created — local files are embedded automatically, so the page keeps",
|
|
95
|
+
"working after your workspace is gone.",
|
|
96
|
+
].join(" "),
|
|
97
|
+
schema: {
|
|
98
|
+
html: z
|
|
99
|
+
.string()
|
|
100
|
+
.describe("Complete standalone HTML document to publish. Inline the CSS and JS."),
|
|
101
|
+
base_dir: z
|
|
102
|
+
.string()
|
|
103
|
+
.optional()
|
|
104
|
+
.describe(
|
|
105
|
+
"Directory that relative image paths resolve against. Defaults to the working directory.",
|
|
106
|
+
),
|
|
107
|
+
},
|
|
108
|
+
async handler(input: { html?: string; base_dir?: string }) {
|
|
109
|
+
const html = input?.html ?? "";
|
|
110
|
+
if (!html.trim()) return errorResult("html is empty — provide an HTML document.");
|
|
111
|
+
|
|
112
|
+
const baseDir = input.base_dir?.trim() || getCtx().cwd || process.cwd();
|
|
113
|
+
const report = inlineLocalAssets(html, {
|
|
114
|
+
baseDir,
|
|
115
|
+
maxAssetBytes: MAX_PUBLISHED_ASSET_BYTES,
|
|
116
|
+
maxTotalBytes: MAX_PUBLISHED_BYTES,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const bytes = Buffer.byteLength(report.html, "utf-8");
|
|
120
|
+
if (bytes > MAX_PUBLISHED_BYTES) {
|
|
121
|
+
return errorResult(
|
|
122
|
+
`Document is ${formatPublishBytes(bytes)} after embedding images, over the ${formatPublishBytes(MAX_PUBLISHED_BYTES)} limit. Use fewer or smaller images, or switch charts to inline <svg>.`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const result = await snippetRequest(apiUrl, "/snippets", {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: { "content-type": "text/html; charset=utf-8" },
|
|
129
|
+
body: report.html,
|
|
130
|
+
});
|
|
131
|
+
if (!result.ok || !result.url) {
|
|
132
|
+
return errorResult(result.error ?? "Failed to publish.");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Surface asset problems here so a broken image is visible now rather
|
|
136
|
+
// than only in the user's browser.
|
|
137
|
+
const notes: string[] = [];
|
|
138
|
+
if (report.inlined.length > 0) {
|
|
139
|
+
const total = report.inlined.reduce((sum, a) => sum + a.bytes, 0);
|
|
140
|
+
notes.push(
|
|
141
|
+
`Embedded ${report.inlined.length} local file(s), ${formatPublishBytes(total)}.`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (report.unresolved.length > 0) {
|
|
145
|
+
notes.push(
|
|
146
|
+
`WARNING — these look like local files but were not found, so they will render broken: ${report.unresolved.join(", ")}. Check the paths (relative to ${baseDir}) and republish.`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
for (const s of report.skipped) {
|
|
150
|
+
notes.push(
|
|
151
|
+
s.reason === "asset-too-large"
|
|
152
|
+
? `WARNING — ${s.ref} is ${formatPublishBytes(s.bytes)}, over the ${formatPublishBytes(MAX_PUBLISHED_ASSET_BYTES)} per-file limit, and was left as a broken link.`
|
|
153
|
+
: `WARNING — ${s.ref} was left out because the document hit the ${formatPublishBytes(MAX_PUBLISHED_BYTES)} limit.`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const payload = {
|
|
158
|
+
url: result.url,
|
|
159
|
+
snippetId: result.snippetId,
|
|
160
|
+
expiresAt: result.expiresAt,
|
|
161
|
+
sizeBytes: bytes,
|
|
162
|
+
...(report.inlined.length > 0 ? { embeddedFiles: report.inlined.length } : {}),
|
|
163
|
+
};
|
|
164
|
+
return textResult([JSON.stringify(payload, null, 2), ...notes].join("\n\n"));
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "unpublish_html",
|
|
169
|
+
description:
|
|
170
|
+
"Delete a previously published HTML document before its 72-hour expiry. Accepts the URL returned by publish_html, or its id.",
|
|
171
|
+
schema: {
|
|
172
|
+
snippet: z.string().describe("URL returned by publish_html, or the bare id."),
|
|
173
|
+
},
|
|
174
|
+
async handler(input: { snippet?: string }) {
|
|
175
|
+
const id = parseSnippetId(input?.snippet ?? "");
|
|
176
|
+
if (!id) return errorResult("Could not read a published document id from that value.");
|
|
177
|
+
const result = await snippetRequest(apiUrl, `/snippets/${id}`, { method: "DELETE" });
|
|
178
|
+
if (!result.ok) return errorResult(result.error ?? "Failed to delete.");
|
|
179
|
+
return textResult(`Published document ${id} deleted.`);
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
];
|
|
183
|
+
}
|
|
@@ -54,6 +54,10 @@ export {
|
|
|
54
54
|
normalizeAskUserChoices,
|
|
55
55
|
normalizeAskUserQuestionInput,
|
|
56
56
|
} from "#agents/mcp-tools/ask-user";
|
|
57
|
+
export {
|
|
58
|
+
createPublishHtmlToolDefinitions,
|
|
59
|
+
type PublishHtmlContext,
|
|
60
|
+
} from "#agents/mcp-tools/publish-html";
|
|
57
61
|
export {
|
|
58
62
|
createSelfConfigRuntime,
|
|
59
63
|
createSelfConfigToolDefinitionsForCore,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Stable core surface used by the embedded runtime MCP endpoint. */
|
|
2
2
|
export { createAskUserToolDefinition } from "#agents/mcp-tools/ask-user";
|
|
3
3
|
export { errorResult, textResult } from "#agents/mcp-tools/common";
|
|
4
|
+
export { createPublishHtmlToolDefinitions } from "#agents/mcp-tools/publish-html";
|
|
4
5
|
export type { SelfConfigContext } from "#agents/mcp-tools/self-config";
|
|
5
6
|
export { createSelfConfigToolDefinitions } from "#agents/mcp-tools/self-config";
|
|
6
7
|
export {
|
|
@@ -153,6 +153,18 @@ export function resolveBrowserRsBin(envValue?: string): string | undefined {
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Backend that stores documents published by `publish_html` and serves them
|
|
158
|
+
* at `<base>/snippets/<id>`. Unset means the deployment has nowhere to
|
|
159
|
+
* publish, and the publish tools are omitted from the catalog rather than
|
|
160
|
+
* offering a link that cannot be minted.
|
|
161
|
+
*/
|
|
162
|
+
export const SNIPPETS_API_URL = (
|
|
163
|
+
envText("NEGOTIUM_SNIPPETS_API_URL") ??
|
|
164
|
+
envText("SNIPPETS_API_URL") ??
|
|
165
|
+
""
|
|
166
|
+
).replace(/\/+$/, "");
|
|
167
|
+
|
|
156
168
|
export const BROWSER_RS_BIN = resolveBrowserRsBin(envText("NEGOTIUM_BROWSER_RS_BIN"));
|
|
157
169
|
|
|
158
170
|
// Managed Browser.rs terminates MCP transports and security policy itself.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const NEGOTIUM_VERSION = "0.2.
|
|
1
|
+
export const NEGOTIUM_VERSION = "0.2.11";
|
package/dist/runtime-helpers.js
CHANGED
|
@@ -494,6 +494,7 @@ function resolveBrowserRsBin(envValue) {
|
|
|
494
494
|
return;
|
|
495
495
|
}
|
|
496
496
|
}
|
|
497
|
+
var SNIPPETS_API_URL = (envText("NEGOTIUM_SNIPPETS_API_URL") ?? envText("SNIPPETS_API_URL") ?? "").replace(/\/+$/, "");
|
|
497
498
|
var BROWSER_RS_BIN = resolveBrowserRsBin(envText("NEGOTIUM_BROWSER_RS_BIN"));
|
|
498
499
|
function resolveBrowserMcpBin(envValue) {
|
|
499
500
|
const override = envValue?.trim();
|
|
@@ -1132,4 +1133,4 @@ export {
|
|
|
1132
1133
|
CLAUDE_EFFORT_VALUES
|
|
1133
1134
|
};
|
|
1134
1135
|
|
|
1135
|
-
//# debugId=
|
|
1136
|
+
//# debugId=842C00B02967857064756E2164756E21
|