jefrichat-mcp 0.49.51 → 0.49.52
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/http.js +80 -19
- package/dist/index.js +72 -13
- package/package.json +1 -1
package/dist/http.js
CHANGED
|
@@ -67197,8 +67197,34 @@ function parseInlineDataUrl(dataUrl) {
|
|
|
67197
67197
|
}
|
|
67198
67198
|
var optionalFileString = external_exports.string().nullish().transform((value) => value ?? void 0);
|
|
67199
67199
|
var REMOTE_FILE_GUIDANCE = "REMOTE connector (ChatGPT, Claude, or another cloud assistant): choose exactly ONE file source. (1) File in YOUR OWN sandbox: check its decoded byte size first. If readable and \u2264256KB (262144 bytes), send directly with `dataUrl` + `fileName`, no user upload step. This includes PDFs, CSVs, code, and small images. Use the actual bytes as canonical base64 in data:<mime>;base64,<base64>; omit `path` and `fileUrl`. (2) Over 256KB, unreadable, or unable to fit the tool call: use jefri_upload_link with the destination and fileName (up to 500 MB per file). If in your sandbox, present the file for download so the user can select it. Never compress, convert, resize, split, or reconstruct a file just to fit the inline limit; never stream base64 in prose. (3) File only on the USER'S DEVICE: use jefri_upload_link; do not read/base64 their local file. (4) An existing public http(s) URL \u2192 `fileUrl` (\u226425MB). Never invent a URL or pass sandbox:/file: URLs. A remote `path` (including /mnt/data) CANNOT read the assistant's sandbox or user's disk; it only creates an upload link, for compatibility. Link creation is NOT file delivery. Omit unused arguments (null is also accepted). If the host blocks a tool call for safety, respect the block; do not retry other arguments to bypass it.";
|
|
67200
|
-
|
|
67201
|
-
|
|
67200
|
+
function hostFileUploadEnabled(ctx) {
|
|
67201
|
+
return ctx.local === false && ctx.credentialLookup?.kind === "profile" && capsForCtx(ctx).has(CAP.REMOTE_SANDBOX) && (ctx.credentialLookup.profile === "grok_bot" || ctx.credentialLookup.profile === "muse");
|
|
67202
|
+
}
|
|
67203
|
+
var HOST_FILE_GUIDANCE = "Grok Bot/Muse REMOTE connector: choose by where the original file bytes live and the tools actually available. If YOUR OWN runtime can read the file and has an allowed outbound HTTP upload tool, use jefri_upload_link with the destination and fileName, then upload the original binary body from YOUR runtime to its scoped POST URL (up to 500 MB per file). Do not ask the user to re-upload a file you can upload yourself. The connector cannot read your runtime or the user's disk; a remote path only prepares an upload grant, never sends bytes. If no permitted binary upload tool is available, readable files \u2264256KB (262144 bytes) can use dataUrl + fileName with canonical data:<mime>;base64,<base64>, omitting path and fileUrl. Otherwise present the file for download and give the user the manual upload link. Files only on the USER'S DEVICE also need that manual upload. An existing public http(s) file URL can use fileUrl (\u226425MB); never invent a URL, use sandbox:/file: URLs, or make a private file public for this. Choose exactly ONE source; omit unused arguments (null is also accepted). Never compress, convert, resize, split, or reconstruct files to fit limits; never print file bytes or large base64 in prose. Link creation is NOT file delivery: confirm the upload's HTTP/JSON receipt; after an uncertain result check the conversation before retrying. Respect host permissions and safety blocks; never switch routes to bypass a block.";
|
|
67204
|
+
function hostUploadUrl(raw, hub) {
|
|
67205
|
+
if (typeof raw !== "string") return null;
|
|
67206
|
+
try {
|
|
67207
|
+
const u = new URL(raw);
|
|
67208
|
+
const base = new URL(hub);
|
|
67209
|
+
return (u.protocol === "https:" || u.protocol === "http:") && u.origin === base.origin && !u.username && !u.password && !u.search && !u.hash && /^\/api\/files\/upload\/[a-f0-9]{48}$/.test(u.pathname) ? u.href : null;
|
|
67210
|
+
} catch {
|
|
67211
|
+
return null;
|
|
67212
|
+
}
|
|
67213
|
+
}
|
|
67214
|
+
function hostUploadInstructions(uploads, where, mins, manualUrl, command) {
|
|
67215
|
+
return `Upload prepared for ${where}. No file has been sent yet. Single-use links expire in ~${mins} min; destination, file name and caption are fixed.
|
|
67216
|
+
${JSON.stringify({ uploads })}
|
|
67217
|
+
|
|
67218
|
+
First check each file's actual size (up to 500 MB) and verify that YOUR OWN runtime can read it and has an allowed outbound HTTP upload tool. If so, upload it yourself now: POST each file's raw binary body to its matching uploadUrl, with its actual Content-Type (application/octet-stream if unknown). No multipart, no base64, no Authorization header, and no redirects. For multiple files, upload sequentially, once per URL; never POST to the batch page or swap file-to-URL assignments. Keep grant URLs private; never send your Jefri token. Use a bounded request with no blind automatic retries. ` + (command ? `If your runtime has an allowed shell AND this exact file, the command is:
|
|
67219
|
+
${command}
|
|
67220
|
+
|
|
67221
|
+
` : "") + `Confirm each delivery only after HTTP success AND JSON ok === true with the delivery id; a prepared link is not success. On a timeout, connection loss, or 5xx, delivery is uncertain: check jefri_history (with for DMs, groupId for groups) before retrying; do not mint a fresh link and resend blindly. A 409/already-used response is not a new delivery. If you cannot verify the outcome, report it as unconfirmed and ask before resending.
|
|
67222
|
+
|
|
67223
|
+
If no permitted binary upload tool exists: readable files \u2264256KB can use dataUrl + fileName via jefri_send_file (jefri_send_group_file for groups); larger/unreadable files need the manual link: ${manualUrl}
|
|
67224
|
+
For manual upload, present a runtime file for download first so the user can pick it. A file only on the user's device requires their upload. Never claim this connector can read their disk or your runtime. Never reconstruct, compress, split, or expose a private file publicly; never inline more than 256KB. Respect all host permissions; a safety denial means stop, not try a different route to bypass it.`;
|
|
67225
|
+
}
|
|
67226
|
+
var fileSendSchema = (local, hostUpload = false) => ({
|
|
67227
|
+
path: optionalFileString.describe(local ? "Local file path to read and send directly, e.g. ~/Downloads/report.pdf. Do not combine with dataUrl or fileUrl." : hostUpload ? "Prepares a scoped upload grant; NEVER reads this path remotely or sends the file. If your runtime can read this file and has an allowed HTTP upload tool, follow the returned binary POST instructions. Otherwise use the manual link." : "Compatibility only: creates an upload link, NEVER reads /mnt/data or any local/sandbox path. For a readable sandbox file \u2264256KB use dataUrl + fileName WITHOUT path; otherwise use jefri_upload_link."),
|
|
67202
67228
|
caption: optionalFileString.describe("optional text caption"),
|
|
67203
67229
|
dataUrl: optionalFileString.describe(local ? "Small content generated in-conversation, max 256KB decoded. Use canonical data:<mime>;base64,<base64> with fileName; omit path and fileUrl. For files on disk use path directly, never read them into base64 yourself." : "Actual content you hold or can read in YOUR OWN sandbox (ChatGPT, Claude apps/Cowork, or another assistant), max 256KB decoded (262144 bytes). Use canonical data:<mime>;base64,<base64> and fileName; omit path and fileUrl. Never base64 a file on the USER machine. Never reconstruct missing bytes or inline files over the cap."),
|
|
67204
67230
|
fileUrl: optionalFileString.describe("An existing public http(s) URL the connector can fetch (\u226425MB), not a sandbox path or invented URL. Do not combine with path or dataUrl."),
|
|
@@ -67211,8 +67237,8 @@ function fileSourceError(args) {
|
|
|
67211
67237
|
return "A nonblank `fileName` is required with `dataUrl`. Nothing was sent.";
|
|
67212
67238
|
return null;
|
|
67213
67239
|
}
|
|
67214
|
-
function missingFileSource(local) {
|
|
67215
|
-
return "Provide exactly one file source: `path`, `dataUrl` + `fileName` (\u2264256KB), or a public `fileUrl`. " + (local ? "On this LOCAL connector, prefer path to send directly from disk." : REMOTE_FILE_GUIDANCE);
|
|
67240
|
+
function missingFileSource(local, hostUpload = false) {
|
|
67241
|
+
return "Provide exactly one file source: `path`, `dataUrl` + `fileName` (\u2264256KB), or a public `fileUrl`. " + (local ? "On this LOCAL connector, prefer path to send directly from disk." : hostUpload ? HOST_FILE_GUIDANCE : REMOTE_FILE_GUIDANCE);
|
|
67216
67242
|
}
|
|
67217
67243
|
var MINT_LINK_DEADLINE_MS = (() => {
|
|
67218
67244
|
const n = Number(process.env.MCP_MINT_LINK_DEADLINE_MS);
|
|
@@ -67299,6 +67325,17 @@ async function remoteUploadCommand(c, ctx, target, path4, caption, where, signal
|
|
|
67299
67325
|
clearTimeout(gKiller);
|
|
67300
67326
|
if (signal) signal.removeEventListener("abort", onHostAbort);
|
|
67301
67327
|
}
|
|
67328
|
+
if (hostFileUploadEnabled(ctx)) {
|
|
67329
|
+
const scoped = hostUploadUrl(uploadUrl, hub);
|
|
67330
|
+
if (!scoped) return { fail: "The upload-link service returned an unexpected upload URL. No file has been sent; do not upload to it." };
|
|
67331
|
+
return { ok: hostUploadInstructions(
|
|
67332
|
+
[{ fileName: fname, uploadUrl: scoped }],
|
|
67333
|
+
where,
|
|
67334
|
+
mins,
|
|
67335
|
+
scoped,
|
|
67336
|
+
`curl --silent --show-error --fail-with-body --connect-timeout 15 --max-time 300 -X POST ${shq(scoped)} -H ${shq(`Content-Type: ${fmime}`)} --data-binary @${shPathArg(path4)}`
|
|
67337
|
+
) };
|
|
67338
|
+
}
|
|
67302
67339
|
{
|
|
67303
67340
|
return {
|
|
67304
67341
|
ok: `This cloud connector can't read that path. No file has been sent.
|
|
@@ -67321,6 +67358,9 @@ Never base64 anything over 256KB through dataUrl \u2014 that is minutes of model
|
|
|
67321
67358
|
}
|
|
67322
67359
|
} catch {
|
|
67323
67360
|
}
|
|
67361
|
+
if (hostFileUploadEnabled(ctx)) return {
|
|
67362
|
+
fail: "The upload-link service did not provide a usable grant. No file has been sent. Do not upload or expose your token. Retry jefri_upload_link when the service recovers; if the host denied permission, stop instead. " + HOST_FILE_GUIDANCE
|
|
67363
|
+
};
|
|
67324
67364
|
const web = hub.replace("acp-hub.", "acp-web.").replace(":4000", ":4321");
|
|
67325
67365
|
const dropLink = target.to ? `${web}/?to=${encodeURIComponent(target.to)}` : `${web}/?group=${encodeURIComponent(target.groupId)}`;
|
|
67326
67366
|
const q = (target.to ? `to=${encodeURIComponent(target.to)}` : `groupId=${encodeURIComponent(target.groupId)}`) + `&fileName=${encodeURIComponent(fname)}` + (caption ? `&caption=${encodeURIComponent(caption)}` : "");
|
|
@@ -68272,6 +68312,8 @@ function ackStandbyDelivered(id4) {
|
|
|
68272
68312
|
}
|
|
68273
68313
|
}
|
|
68274
68314
|
function registerAcpTools(server2, ctx) {
|
|
68315
|
+
const hostFileUpload = hostFileUploadEnabled(ctx);
|
|
68316
|
+
const remoteFileGuidance = hostFileUpload ? HOST_FILE_GUIDANCE : REMOTE_FILE_GUIDANCE;
|
|
68275
68317
|
const desktopHeadlessOnly = isHeadlessOnlyDesktop(ctx.credentialLookup?.kind === "profile" ? ctx.credentialLookup.profile : ctx.profile);
|
|
68276
68318
|
const autonomyProfile = ctx.credentialLookup?.kind === "profile" ? ctx.credentialLookup.profile : ctx.profile;
|
|
68277
68319
|
const backgroundOnly = isBackgroundOnlyAutonomyProfile(autonomyProfile);
|
|
@@ -68709,12 +68751,12 @@ ${fp}`);
|
|
|
68709
68751
|
"jefri_upload_link",
|
|
68710
68752
|
{
|
|
68711
68753
|
title: "Get an upload link (any file up to 500 MB, full quality)",
|
|
68712
|
-
description: "Get a ONE-TAP UPLOAD LINK to send files or images \u2014 up to 5 PER LINK via `fileNames`, up to 500 MB each, FULL quality, no base64, no compression, no path needed. For ChatGPT, Claude, and other remote assistants: small files \u2264256KB (already held OR readable in YOUR OWN sandbox \u2014 PDFs, CSVs, code, small images) go directly through jefri_send_file (or jefri_send_group_file for groups) with dataUrl + fileName ONLY, no path or link needed. Use THIS tool for larger files, unreadable files, files that cannot fit the tool call, or files only on the user's device. One cheap call, then hand the user the link \u2014 they open it (phone works), pick the file, and it delivers to the recipient automatically. Give `to` (a username) or `groupId`, plus the `fileName` the delivery should carry. The link is single-use and expires in ~15 minutes; mint a fresh one any time. If the file lives in YOUR sandbox, present it to the user for download first so they can pick it at the link. NEVER stream file bytes through plain output (base64/pixel data) \u2014 that burns the user's tokens. The ONE sanctioned carrier is a \u2264256KB `dataUrl` TOOL ARGUMENT via jefri_send_file (near the cap it may still strain output limits \u2014 then use this link); anything larger only ever travels as this link. Creating the link is NOT delivery \u2014 the user must upload the file. Respect host safety blocks; do not use another route to bypass one. Omit unused fields or pass null.",
|
|
68754
|
+
description: hostFileUpload ? "Prepare scoped upload URLs, up to 5 files via fileNames (500 MB each). Give to OR groupId, plus fileName OR fileNames. Each URL is single-use, expires in ~15 minutes, and fixes the destination, name and caption. " + HOST_FILE_GUIDANCE : "Get a ONE-TAP UPLOAD LINK to send files or images \u2014 up to 5 PER LINK via `fileNames`, up to 500 MB each, FULL quality, no base64, no compression, no path needed. For ChatGPT, Claude, and other remote assistants: small files \u2264256KB (already held OR readable in YOUR OWN sandbox \u2014 PDFs, CSVs, code, small images) go directly through jefri_send_file (or jefri_send_group_file for groups) with dataUrl + fileName ONLY, no path or link needed. Use THIS tool for larger files, unreadable files, files that cannot fit the tool call, or files only on the user's device. One cheap call, then hand the user the link \u2014 they open it (phone works), pick the file, and it delivers to the recipient automatically. Give `to` (a username) or `groupId`, plus the `fileName` the delivery should carry. The link is single-use and expires in ~15 minutes; mint a fresh one any time. If the file lives in YOUR sandbox, present it to the user for download first so they can pick it at the link. NEVER stream file bytes through plain output (base64/pixel data) \u2014 that burns the user's tokens. The ONE sanctioned carrier is a \u2264256KB `dataUrl` TOOL ARGUMENT via jefri_send_file (near the cap it may still strain output limits \u2014 then use this link); anything larger only ever travels as this link. Creating the link is NOT delivery \u2014 the user must upload the file. Respect host safety blocks; do not use another route to bypass one. Omit unused fields or pass null.",
|
|
68713
68755
|
inputSchema: {
|
|
68714
68756
|
to: optionalFileString.describe("recipient username (or use groupId)"),
|
|
68715
68757
|
groupId: optionalFileString.describe("group id (from jefri_groups) instead of `to`"),
|
|
68716
68758
|
fileName: optionalFileString.describe("the name the delivered file should carry, e.g. photo.png (single file)"),
|
|
68717
|
-
fileNames: external_exports.array(external_exports.string()).nullish().transform((value) => value ?? void 0).describe('for a MULTI-file link (2\u20135): the delivery names in order, e.g. ["foto-1.jpg","foto-2.jpg"] \u2014 one link, the user picks them all'),
|
|
68759
|
+
fileNames: external_exports.array(external_exports.string()).nullish().transform((value) => value ?? void 0).describe(hostFileUpload ? "For 2\u20135 files: delivery names in order. Returns one binary POST URL per file plus a manual batch page." : 'for a MULTI-file link (2\u20135): the delivery names in order, e.g. ["foto-1.jpg","foto-2.jpg"] \u2014 one link, the user picks them all'),
|
|
68718
68760
|
caption: optionalFileString.describe("optional text caption delivered with the file")
|
|
68719
68761
|
}
|
|
68720
68762
|
},
|
|
@@ -68771,6 +68813,23 @@ ${fp}`);
|
|
|
68771
68813
|
const mins = Math.round(body.expiresInMs / 6e4);
|
|
68772
68814
|
const where = to ? `@${safeHandle(to)}` : "the group";
|
|
68773
68815
|
const delivers = names.length ? `${names.length} files: ${names.map((n) => `\u{1F4CE} ${n}`).join(", ")}` : `\u{1F4CE} ${fileName}`;
|
|
68816
|
+
if (hostFileUpload) {
|
|
68817
|
+
const expectedNames = (names.length ? names : [fileName]).map((name) => (name.split(/[\\/]/).pop() ?? "file").slice(0, 300) || "file");
|
|
68818
|
+
const rawUploads = names.length ? body.uploads : [{ fileName: expectedNames[0], uploadUrl: body.uploadUrl }];
|
|
68819
|
+
if (!Array.isArray(rawUploads) || rawUploads.length !== expectedNames.length)
|
|
68820
|
+
return fail("The upload-link service returned incomplete file upload data. No file has been sent; do not upload to it.");
|
|
68821
|
+
const uploads = [];
|
|
68822
|
+
for (const [i, upload] of rawUploads.entries()) {
|
|
68823
|
+
const scoped = hostUploadUrl(upload?.uploadUrl, hub);
|
|
68824
|
+
if (!scoped || upload?.fileName !== expectedNames[i] || uploads.some((u) => u.uploadUrl === scoped))
|
|
68825
|
+
return fail("The upload-link service returned unexpected file upload data. No file has been sent; do not upload to it.");
|
|
68826
|
+
uploads.push({ fileName: expectedNames[i], uploadUrl: scoped });
|
|
68827
|
+
}
|
|
68828
|
+
const expectedManual = names.length ? `${hub}/api/files/upload-batch/${uploads.map((u) => u.uploadUrl.split("/").pop()).join(".")}` : uploads[0].uploadUrl;
|
|
68829
|
+
if (parsedUrl !== expectedManual)
|
|
68830
|
+
return fail("The upload-link service returned an unexpected manual upload URL. No file has been sent; do not upload to it.");
|
|
68831
|
+
return ok3(hostUploadInstructions(uploads, where, mins, expectedManual));
|
|
68832
|
+
}
|
|
68774
68833
|
return ok3(
|
|
68775
68834
|
`\u{1F517} One-tap upload link for ${where} (delivers ${delivers}; valid ~${mins} min, single-use):
|
|
68776
68835
|
${parsedUrl}
|
|
@@ -68883,10 +68942,10 @@ ${lines.join("\n")}`
|
|
|
68883
68942
|
"jefri_send_file",
|
|
68884
68943
|
{
|
|
68885
68944
|
title: "Send a file, PDF, or image",
|
|
68886
|
-
description: ctx.local ? "Send a file to a Jefri Chat user/agent \u2014 PDF, image, video, any document. On this LOCAL connector, `path` reads the file off disk and sends it DIRECTLY (up to 500 MB) \u2014 always prefer `path`; never read a file into base64 yourself. `dataUrl` + `fileName` is only for small (\u2264256KB) content you generated in-conversation, and `fileUrl` fetches a public URL. Optional `caption` always." : "Send a file to a Jefri Chat user/agent. " +
|
|
68945
|
+
description: ctx.local ? "Send a file to a Jefri Chat user/agent \u2014 PDF, image, video, any document. On this LOCAL connector, `path` reads the file off disk and sends it DIRECTLY (up to 500 MB) \u2014 always prefer `path`; never read a file into base64 yourself. `dataUrl` + `fileName` is only for small (\u2264256KB) content you generated in-conversation, and `fileUrl` fetches a public URL. Optional `caption` always." : "Send a file to a Jefri Chat user/agent. " + remoteFileGuidance,
|
|
68887
68946
|
inputSchema: {
|
|
68888
68947
|
to: external_exports.string().describe("recipient username"),
|
|
68889
|
-
...fileSendSchema(!!ctx.local)
|
|
68948
|
+
...fileSendSchema(!!ctx.local, hostFileUpload)
|
|
68890
68949
|
}
|
|
68891
68950
|
},
|
|
68892
68951
|
async ({ to, path: path4, caption, dataUrl, fileUrl, fileName }, extra) => withClient(async (c) => {
|
|
@@ -68921,7 +68980,7 @@ ${lines.join("\n")}`
|
|
|
68921
68980
|
return fail(`Couldn't fetch that URL: ${e2?.message ?? e2}`);
|
|
68922
68981
|
}
|
|
68923
68982
|
} else {
|
|
68924
|
-
return fail(missingFileSource(!!ctx.local));
|
|
68983
|
+
return fail(missingFileSource(!!ctx.local, hostFileUpload));
|
|
68925
68984
|
}
|
|
68926
68985
|
const rawBytes = inlineBytes ?? Buffer.from(url.split(",")[1] ?? "", "base64");
|
|
68927
68986
|
if (rawBytes.length > MAX_UPLOAD_BYTES)
|
|
@@ -69745,10 +69804,10 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
69745
69804
|
"jefri_send_group_file",
|
|
69746
69805
|
{
|
|
69747
69806
|
title: "Send a file to a group",
|
|
69748
|
-
description: ctx.local ? "Send a file/image to a GROUP by its group id (from jefri_groups / jefri_inbox). On this LOCAL connector, `path` sends the file DIRECTLY off disk (up to 500 MB) \u2014 always prefer `path`; `dataUrl` only for small generated content; `fileUrl` for a public URL." : "Send a file/image to a GROUP by its group id (from jefri_groups / jefri_inbox). " +
|
|
69807
|
+
description: ctx.local ? "Send a file/image to a GROUP by its group id (from jefri_groups / jefri_inbox). On this LOCAL connector, `path` sends the file DIRECTLY off disk (up to 500 MB) \u2014 always prefer `path`; `dataUrl` only for small generated content; `fileUrl` for a public URL." : "Send a file/image to a GROUP by its group id (from jefri_groups / jefri_inbox). " + remoteFileGuidance,
|
|
69749
69808
|
inputSchema: {
|
|
69750
69809
|
groupId: external_exports.string().describe("the group's id (from jefri_groups or jefri_inbox)"),
|
|
69751
|
-
...fileSendSchema(!!ctx.local)
|
|
69810
|
+
...fileSendSchema(!!ctx.local, hostFileUpload)
|
|
69752
69811
|
}
|
|
69753
69812
|
},
|
|
69754
69813
|
async ({ groupId, path: path4, caption, dataUrl, fileUrl, fileName }, extra) => withClient(async (c) => {
|
|
@@ -69781,7 +69840,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
69781
69840
|
return fail(`Couldn't fetch that URL: ${e2?.message ?? e2}`);
|
|
69782
69841
|
}
|
|
69783
69842
|
} else {
|
|
69784
|
-
return fail(missingFileSource(!!ctx.local));
|
|
69843
|
+
return fail(missingFileSource(!!ctx.local, hostFileUpload));
|
|
69785
69844
|
}
|
|
69786
69845
|
const rawBytes = inlineBytes ?? Buffer.from(url.split(",")[1] ?? "", "base64");
|
|
69787
69846
|
if (rawBytes.length > MAX_UPLOAD_BYTES)
|
|
@@ -71561,15 +71620,16 @@ function registerGrokMessageTool(server2, options) {
|
|
|
71561
71620
|
if (options.lookup.kind !== "profile" || options.lookup.profile !== "grok_bot") return;
|
|
71562
71621
|
server2.registerTool("jefri_grok_read_message", {
|
|
71563
71622
|
title: "Read the exact message behind a Grok webhook",
|
|
71564
|
-
description: "Read one plain incoming DM by the webhook's from and hubMessageId (or captionMessageId). Works after inbox acknowledgement. Read-only; never marks anything seen. Verify jefri_whoami matches the event's agent and server first. For a file, use jefri_download_file with its exact messageId to retrieve bytes.",
|
|
71623
|
+
description: "Read one plain incoming DM or group message by the webhook's from and hubMessageId (or captionMessageId). Pass groupId only when present in the event; reply to that same group with jefri_send_group, never a DM fallback. Works after inbox acknowledgement. Read-only; never marks anything seen. Verify jefri_whoami matches the event's agent and server first. For a file, use jefri_download_file with its exact messageId and groupId when present to retrieve bytes.",
|
|
71565
71624
|
inputSchema: {
|
|
71566
71625
|
from: external_exports.string().min(1).max(120).describe("Exact sender username from the webhook"),
|
|
71567
|
-
messageId: external_exports.string().min(1).max(200).describe("Exact hubMessageId or captionMessageId from the webhook")
|
|
71626
|
+
messageId: external_exports.string().min(1).max(200).describe("Exact hubMessageId or captionMessageId from the webhook"),
|
|
71627
|
+
groupId: external_exports.string().min(1).max(200).optional().describe("Exact groupId from a group webhook; omit for DMs")
|
|
71568
71628
|
},
|
|
71569
71629
|
annotations: { readOnlyHint: true }
|
|
71570
|
-
}, async ({ from, messageId }, extra) => {
|
|
71630
|
+
}, async ({ from, messageId, groupId }, extra) => {
|
|
71571
71631
|
const fail2 = (text5) => ({ isError: true, content: [{ type: "text", text: text5 }] });
|
|
71572
|
-
if (!isRoutingKey(from) || !/^[A-Za-z0-9_-]{1,200}$/.test(messageId)) return fail2("Invalid sender or
|
|
71632
|
+
if (!isRoutingKey(from) || !/^[A-Za-z0-9_-]{1,200}$/.test(messageId) || groupId !== void 0 && !/^[A-Za-z0-9_-]{1,200}$/.test(groupId)) return fail2("Invalid sender, message or group id.");
|
|
71573
71633
|
const me2 = options.client.identity?.username;
|
|
71574
71634
|
if (!me2 || !options.client.token) return fail2("Jefri identity is not verified. Reconnect this Grok connector.");
|
|
71575
71635
|
const controller = new AbortController();
|
|
@@ -71579,7 +71639,7 @@ function registerGrokMessageTool(server2, options) {
|
|
|
71579
71639
|
const timeout = setTimeout(onAbort, 1e4);
|
|
71580
71640
|
try {
|
|
71581
71641
|
const url = new URL(options.hub.replace(/\/$/, "") + "/api/history");
|
|
71582
|
-
url.searchParams.set("conversationId", dmConversationId(me2, from));
|
|
71642
|
+
url.searchParams.set("conversationId", groupId ? groupConversationId(groupId) : dmConversationId(me2, from));
|
|
71583
71643
|
url.searchParams.set("around", messageId);
|
|
71584
71644
|
url.searchParams.set("peek", "1");
|
|
71585
71645
|
const response = await (options.fetch ?? fetch)(url, {
|
|
@@ -71590,13 +71650,14 @@ function registerGrokMessageTool(server2, options) {
|
|
|
71590
71650
|
if (!response.ok || !response.body) return fail2("The exact message is unavailable or access was denied. Do not substitute another message.");
|
|
71591
71651
|
const data = JSON.parse((await readCapped(response.body, 2 * 1024 * 1024)).toString("utf8"));
|
|
71592
71652
|
const row = Array.isArray(data.messages) ? data.messages.find((m) => m.id === messageId) : null;
|
|
71593
|
-
if (!row || row.senderUsername !== from || row.
|
|
71594
|
-
return fail2("No matching plain incoming
|
|
71653
|
+
if (!row || row.senderUsername !== from || row.debateId || row.encryptionMode !== "audited" || (groupId ? row.groupId !== groupId || row.receiver != null : row.receiver !== me2 || !!row.groupId) || !["text", "file"].includes(row.kind) || typeof row.content !== "string")
|
|
71654
|
+
return fail2("No matching plain incoming message was found. Do not substitute another message.");
|
|
71595
71655
|
const message = {
|
|
71596
71656
|
id: row.id,
|
|
71597
71657
|
from: row.senderUsername,
|
|
71598
71658
|
agent: me2,
|
|
71599
71659
|
kind: row.kind,
|
|
71660
|
+
...groupId ? { groupId } : {},
|
|
71600
71661
|
text: row.kind === "text" ? row.content : null,
|
|
71601
71662
|
fileId: row.kind === "file" ? row.id : null,
|
|
71602
71663
|
fileName: row.kind === "file" ? row.fileName : null,
|
package/dist/index.js
CHANGED
|
@@ -59492,6 +59492,31 @@ function parseInlineDataUrl(dataUrl) {
|
|
|
59492
59492
|
};
|
|
59493
59493
|
return { mime: m[1], bytes: bytes2 };
|
|
59494
59494
|
}
|
|
59495
|
+
function hostFileUploadEnabled(ctx) {
|
|
59496
|
+
return ctx.local === false && ctx.credentialLookup?.kind === "profile" && capsForCtx(ctx).has(CAP.REMOTE_SANDBOX) && (ctx.credentialLookup.profile === "grok_bot" || ctx.credentialLookup.profile === "muse");
|
|
59497
|
+
}
|
|
59498
|
+
function hostUploadUrl(raw, hub) {
|
|
59499
|
+
if (typeof raw !== "string") return null;
|
|
59500
|
+
try {
|
|
59501
|
+
const u = new URL(raw);
|
|
59502
|
+
const base = new URL(hub);
|
|
59503
|
+
return (u.protocol === "https:" || u.protocol === "http:") && u.origin === base.origin && !u.username && !u.password && !u.search && !u.hash && /^\/api\/files\/upload\/[a-f0-9]{48}$/.test(u.pathname) ? u.href : null;
|
|
59504
|
+
} catch {
|
|
59505
|
+
return null;
|
|
59506
|
+
}
|
|
59507
|
+
}
|
|
59508
|
+
function hostUploadInstructions(uploads, where, mins, manualUrl, command) {
|
|
59509
|
+
return `Upload prepared for ${where}. No file has been sent yet. Single-use links expire in ~${mins} min; destination, file name and caption are fixed.
|
|
59510
|
+
${JSON.stringify({ uploads })}
|
|
59511
|
+
|
|
59512
|
+
First check each file's actual size (up to 500 MB) and verify that YOUR OWN runtime can read it and has an allowed outbound HTTP upload tool. If so, upload it yourself now: POST each file's raw binary body to its matching uploadUrl, with its actual Content-Type (application/octet-stream if unknown). No multipart, no base64, no Authorization header, and no redirects. For multiple files, upload sequentially, once per URL; never POST to the batch page or swap file-to-URL assignments. Keep grant URLs private; never send your Jefri token. Use a bounded request with no blind automatic retries. ` + (command ? `If your runtime has an allowed shell AND this exact file, the command is:
|
|
59513
|
+
${command}
|
|
59514
|
+
|
|
59515
|
+
` : "") + `Confirm each delivery only after HTTP success AND JSON ok === true with the delivery id; a prepared link is not success. On a timeout, connection loss, or 5xx, delivery is uncertain: check jefri_history (with for DMs, groupId for groups) before retrying; do not mint a fresh link and resend blindly. A 409/already-used response is not a new delivery. If you cannot verify the outcome, report it as unconfirmed and ask before resending.
|
|
59516
|
+
|
|
59517
|
+
If no permitted binary upload tool exists: readable files \u2264256KB can use dataUrl + fileName via jefri_send_file (jefri_send_group_file for groups); larger/unreadable files need the manual link: ${manualUrl}
|
|
59518
|
+
For manual upload, present a runtime file for download first so the user can pick it. A file only on the user's device requires their upload. Never claim this connector can read their disk or your runtime. Never reconstruct, compress, split, or expose a private file publicly; never inline more than 256KB. Respect all host permissions; a safety denial means stop, not try a different route to bypass it.`;
|
|
59519
|
+
}
|
|
59495
59520
|
function fileSourceError(args) {
|
|
59496
59521
|
if ([args.path, args.dataUrl, args.fileUrl].filter(Boolean).length > 1)
|
|
59497
59522
|
return "Choose exactly ONE file source: `path`, `dataUrl` + `fileName`, or `fileUrl`. Nothing was sent. For a readable sandbox file \u2264256KB, use `dataUrl` + `fileName` WITHOUT `path` or `fileUrl`.";
|
|
@@ -59499,8 +59524,8 @@ function fileSourceError(args) {
|
|
|
59499
59524
|
return "A nonblank `fileName` is required with `dataUrl`. Nothing was sent.";
|
|
59500
59525
|
return null;
|
|
59501
59526
|
}
|
|
59502
|
-
function missingFileSource(local) {
|
|
59503
|
-
return "Provide exactly one file source: `path`, `dataUrl` + `fileName` (\u2264256KB), or a public `fileUrl`. " + (local ? "On this LOCAL connector, prefer path to send directly from disk." : REMOTE_FILE_GUIDANCE);
|
|
59527
|
+
function missingFileSource(local, hostUpload = false) {
|
|
59528
|
+
return "Provide exactly one file source: `path`, `dataUrl` + `fileName` (\u2264256KB), or a public `fileUrl`. " + (local ? "On this LOCAL connector, prefer path to send directly from disk." : hostUpload ? HOST_FILE_GUIDANCE : REMOTE_FILE_GUIDANCE);
|
|
59504
59529
|
}
|
|
59505
59530
|
async function mintFileLink(hub, token, fileId) {
|
|
59506
59531
|
const ac = new AbortController();
|
|
@@ -59580,6 +59605,17 @@ async function remoteUploadCommand(c2, ctx, target, path39, caption, where, sign
|
|
|
59580
59605
|
clearTimeout(gKiller);
|
|
59581
59606
|
if (signal) signal.removeEventListener("abort", onHostAbort);
|
|
59582
59607
|
}
|
|
59608
|
+
if (hostFileUploadEnabled(ctx)) {
|
|
59609
|
+
const scoped = hostUploadUrl(uploadUrl, hub);
|
|
59610
|
+
if (!scoped) return { fail: "The upload-link service returned an unexpected upload URL. No file has been sent; do not upload to it." };
|
|
59611
|
+
return { ok: hostUploadInstructions(
|
|
59612
|
+
[{ fileName: fname, uploadUrl: scoped }],
|
|
59613
|
+
where,
|
|
59614
|
+
mins,
|
|
59615
|
+
scoped,
|
|
59616
|
+
`curl --silent --show-error --fail-with-body --connect-timeout 15 --max-time 300 -X POST ${shq(scoped)} -H ${shq(`Content-Type: ${fmime}`)} --data-binary @${shPathArg(path39)}`
|
|
59617
|
+
) };
|
|
59618
|
+
}
|
|
59583
59619
|
{
|
|
59584
59620
|
return {
|
|
59585
59621
|
ok: `This cloud connector can't read that path. No file has been sent.
|
|
@@ -59602,6 +59638,9 @@ Never base64 anything over 256KB through dataUrl \u2014 that is minutes of model
|
|
|
59602
59638
|
}
|
|
59603
59639
|
} catch {
|
|
59604
59640
|
}
|
|
59641
|
+
if (hostFileUploadEnabled(ctx)) return {
|
|
59642
|
+
fail: "The upload-link service did not provide a usable grant. No file has been sent. Do not upload or expose your token. Retry jefri_upload_link when the service recovers; if the host denied permission, stop instead. " + HOST_FILE_GUIDANCE
|
|
59643
|
+
};
|
|
59605
59644
|
const web = hub.replace("acp-hub.", "acp-web.").replace(":4000", ":4321");
|
|
59606
59645
|
const dropLink = target.to ? `${web}/?to=${encodeURIComponent(target.to)}` : `${web}/?group=${encodeURIComponent(target.groupId)}`;
|
|
59607
59646
|
const q = (target.to ? `to=${encodeURIComponent(target.to)}` : `groupId=${encodeURIComponent(target.groupId)}`) + `&fileName=${encodeURIComponent(fname)}` + (caption ? `&caption=${encodeURIComponent(caption)}` : "");
|
|
@@ -60349,6 +60388,8 @@ function ackStandbyDelivered(id5) {
|
|
|
60349
60388
|
}
|
|
60350
60389
|
}
|
|
60351
60390
|
function registerAcpTools(server2, ctx) {
|
|
60391
|
+
const hostFileUpload = hostFileUploadEnabled(ctx);
|
|
60392
|
+
const remoteFileGuidance = hostFileUpload ? HOST_FILE_GUIDANCE : REMOTE_FILE_GUIDANCE;
|
|
60352
60393
|
const desktopHeadlessOnly = isHeadlessOnlyDesktop(ctx.credentialLookup?.kind === "profile" ? ctx.credentialLookup.profile : ctx.profile);
|
|
60353
60394
|
const autonomyProfile = ctx.credentialLookup?.kind === "profile" ? ctx.credentialLookup.profile : ctx.profile;
|
|
60354
60395
|
const backgroundOnly = isBackgroundOnlyAutonomyProfile(autonomyProfile);
|
|
@@ -60786,12 +60827,12 @@ ${fp}`);
|
|
|
60786
60827
|
"jefri_upload_link",
|
|
60787
60828
|
{
|
|
60788
60829
|
title: "Get an upload link (any file up to 500 MB, full quality)",
|
|
60789
|
-
description: "Get a ONE-TAP UPLOAD LINK to send files or images \u2014 up to 5 PER LINK via `fileNames`, up to 500 MB each, FULL quality, no base64, no compression, no path needed. For ChatGPT, Claude, and other remote assistants: small files \u2264256KB (already held OR readable in YOUR OWN sandbox \u2014 PDFs, CSVs, code, small images) go directly through jefri_send_file (or jefri_send_group_file for groups) with dataUrl + fileName ONLY, no path or link needed. Use THIS tool for larger files, unreadable files, files that cannot fit the tool call, or files only on the user's device. One cheap call, then hand the user the link \u2014 they open it (phone works), pick the file, and it delivers to the recipient automatically. Give `to` (a username) or `groupId`, plus the `fileName` the delivery should carry. The link is single-use and expires in ~15 minutes; mint a fresh one any time. If the file lives in YOUR sandbox, present it to the user for download first so they can pick it at the link. NEVER stream file bytes through plain output (base64/pixel data) \u2014 that burns the user's tokens. The ONE sanctioned carrier is a \u2264256KB `dataUrl` TOOL ARGUMENT via jefri_send_file (near the cap it may still strain output limits \u2014 then use this link); anything larger only ever travels as this link. Creating the link is NOT delivery \u2014 the user must upload the file. Respect host safety blocks; do not use another route to bypass one. Omit unused fields or pass null.",
|
|
60830
|
+
description: hostFileUpload ? "Prepare scoped upload URLs, up to 5 files via fileNames (500 MB each). Give to OR groupId, plus fileName OR fileNames. Each URL is single-use, expires in ~15 minutes, and fixes the destination, name and caption. " + HOST_FILE_GUIDANCE : "Get a ONE-TAP UPLOAD LINK to send files or images \u2014 up to 5 PER LINK via `fileNames`, up to 500 MB each, FULL quality, no base64, no compression, no path needed. For ChatGPT, Claude, and other remote assistants: small files \u2264256KB (already held OR readable in YOUR OWN sandbox \u2014 PDFs, CSVs, code, small images) go directly through jefri_send_file (or jefri_send_group_file for groups) with dataUrl + fileName ONLY, no path or link needed. Use THIS tool for larger files, unreadable files, files that cannot fit the tool call, or files only on the user's device. One cheap call, then hand the user the link \u2014 they open it (phone works), pick the file, and it delivers to the recipient automatically. Give `to` (a username) or `groupId`, plus the `fileName` the delivery should carry. The link is single-use and expires in ~15 minutes; mint a fresh one any time. If the file lives in YOUR sandbox, present it to the user for download first so they can pick it at the link. NEVER stream file bytes through plain output (base64/pixel data) \u2014 that burns the user's tokens. The ONE sanctioned carrier is a \u2264256KB `dataUrl` TOOL ARGUMENT via jefri_send_file (near the cap it may still strain output limits \u2014 then use this link); anything larger only ever travels as this link. Creating the link is NOT delivery \u2014 the user must upload the file. Respect host safety blocks; do not use another route to bypass one. Omit unused fields or pass null.",
|
|
60790
60831
|
inputSchema: {
|
|
60791
60832
|
to: optionalFileString.describe("recipient username (or use groupId)"),
|
|
60792
60833
|
groupId: optionalFileString.describe("group id (from jefri_groups) instead of `to`"),
|
|
60793
60834
|
fileName: optionalFileString.describe("the name the delivered file should carry, e.g. photo.png (single file)"),
|
|
60794
|
-
fileNames: external_exports.array(external_exports.string()).nullish().transform((value) => value ?? void 0).describe('for a MULTI-file link (2\u20135): the delivery names in order, e.g. ["foto-1.jpg","foto-2.jpg"] \u2014 one link, the user picks them all'),
|
|
60835
|
+
fileNames: external_exports.array(external_exports.string()).nullish().transform((value) => value ?? void 0).describe(hostFileUpload ? "For 2\u20135 files: delivery names in order. Returns one binary POST URL per file plus a manual batch page." : 'for a MULTI-file link (2\u20135): the delivery names in order, e.g. ["foto-1.jpg","foto-2.jpg"] \u2014 one link, the user picks them all'),
|
|
60795
60836
|
caption: optionalFileString.describe("optional text caption delivered with the file")
|
|
60796
60837
|
}
|
|
60797
60838
|
},
|
|
@@ -60848,6 +60889,23 @@ ${fp}`);
|
|
|
60848
60889
|
const mins = Math.round(body.expiresInMs / 6e4);
|
|
60849
60890
|
const where = to ? `@${safeHandle(to)}` : "the group";
|
|
60850
60891
|
const delivers = names.length ? `${names.length} files: ${names.map((n) => `\u{1F4CE} ${n}`).join(", ")}` : `\u{1F4CE} ${fileName}`;
|
|
60892
|
+
if (hostFileUpload) {
|
|
60893
|
+
const expectedNames = (names.length ? names : [fileName]).map((name) => (name.split(/[\\/]/).pop() ?? "file").slice(0, 300) || "file");
|
|
60894
|
+
const rawUploads = names.length ? body.uploads : [{ fileName: expectedNames[0], uploadUrl: body.uploadUrl }];
|
|
60895
|
+
if (!Array.isArray(rawUploads) || rawUploads.length !== expectedNames.length)
|
|
60896
|
+
return fail("The upload-link service returned incomplete file upload data. No file has been sent; do not upload to it.");
|
|
60897
|
+
const uploads = [];
|
|
60898
|
+
for (const [i, upload] of rawUploads.entries()) {
|
|
60899
|
+
const scoped = hostUploadUrl(upload?.uploadUrl, hub);
|
|
60900
|
+
if (!scoped || upload?.fileName !== expectedNames[i] || uploads.some((u) => u.uploadUrl === scoped))
|
|
60901
|
+
return fail("The upload-link service returned unexpected file upload data. No file has been sent; do not upload to it.");
|
|
60902
|
+
uploads.push({ fileName: expectedNames[i], uploadUrl: scoped });
|
|
60903
|
+
}
|
|
60904
|
+
const expectedManual = names.length ? `${hub}/api/files/upload-batch/${uploads.map((u) => u.uploadUrl.split("/").pop()).join(".")}` : uploads[0].uploadUrl;
|
|
60905
|
+
if (parsedUrl !== expectedManual)
|
|
60906
|
+
return fail("The upload-link service returned an unexpected manual upload URL. No file has been sent; do not upload to it.");
|
|
60907
|
+
return ok3(hostUploadInstructions(uploads, where, mins, expectedManual));
|
|
60908
|
+
}
|
|
60851
60909
|
return ok3(
|
|
60852
60910
|
`\u{1F517} One-tap upload link for ${where} (delivers ${delivers}; valid ~${mins} min, single-use):
|
|
60853
60911
|
${parsedUrl}
|
|
@@ -60960,10 +61018,10 @@ ${lines.join("\n")}`
|
|
|
60960
61018
|
"jefri_send_file",
|
|
60961
61019
|
{
|
|
60962
61020
|
title: "Send a file, PDF, or image",
|
|
60963
|
-
description: ctx.local ? "Send a file to a Jefri Chat user/agent \u2014 PDF, image, video, any document. On this LOCAL connector, `path` reads the file off disk and sends it DIRECTLY (up to 500 MB) \u2014 always prefer `path`; never read a file into base64 yourself. `dataUrl` + `fileName` is only for small (\u2264256KB) content you generated in-conversation, and `fileUrl` fetches a public URL. Optional `caption` always." : "Send a file to a Jefri Chat user/agent. " +
|
|
61021
|
+
description: ctx.local ? "Send a file to a Jefri Chat user/agent \u2014 PDF, image, video, any document. On this LOCAL connector, `path` reads the file off disk and sends it DIRECTLY (up to 500 MB) \u2014 always prefer `path`; never read a file into base64 yourself. `dataUrl` + `fileName` is only for small (\u2264256KB) content you generated in-conversation, and `fileUrl` fetches a public URL. Optional `caption` always." : "Send a file to a Jefri Chat user/agent. " + remoteFileGuidance,
|
|
60964
61022
|
inputSchema: {
|
|
60965
61023
|
to: external_exports.string().describe("recipient username"),
|
|
60966
|
-
...fileSendSchema(!!ctx.local)
|
|
61024
|
+
...fileSendSchema(!!ctx.local, hostFileUpload)
|
|
60967
61025
|
}
|
|
60968
61026
|
},
|
|
60969
61027
|
async ({ to, path: path39, caption, dataUrl, fileUrl, fileName }, extra) => withClient(async (c2) => {
|
|
@@ -60998,7 +61056,7 @@ ${lines.join("\n")}`
|
|
|
60998
61056
|
return fail(`Couldn't fetch that URL: ${e2?.message ?? e2}`);
|
|
60999
61057
|
}
|
|
61000
61058
|
} else {
|
|
61001
|
-
return fail(missingFileSource(!!ctx.local));
|
|
61059
|
+
return fail(missingFileSource(!!ctx.local, hostFileUpload));
|
|
61002
61060
|
}
|
|
61003
61061
|
const rawBytes = inlineBytes ?? Buffer.from(url.split(",")[1] ?? "", "base64");
|
|
61004
61062
|
if (rawBytes.length > MAX_UPLOAD_BYTES)
|
|
@@ -61822,10 +61880,10 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
61822
61880
|
"jefri_send_group_file",
|
|
61823
61881
|
{
|
|
61824
61882
|
title: "Send a file to a group",
|
|
61825
|
-
description: ctx.local ? "Send a file/image to a GROUP by its group id (from jefri_groups / jefri_inbox). On this LOCAL connector, `path` sends the file DIRECTLY off disk (up to 500 MB) \u2014 always prefer `path`; `dataUrl` only for small generated content; `fileUrl` for a public URL." : "Send a file/image to a GROUP by its group id (from jefri_groups / jefri_inbox). " +
|
|
61883
|
+
description: ctx.local ? "Send a file/image to a GROUP by its group id (from jefri_groups / jefri_inbox). On this LOCAL connector, `path` sends the file DIRECTLY off disk (up to 500 MB) \u2014 always prefer `path`; `dataUrl` only for small generated content; `fileUrl` for a public URL." : "Send a file/image to a GROUP by its group id (from jefri_groups / jefri_inbox). " + remoteFileGuidance,
|
|
61826
61884
|
inputSchema: {
|
|
61827
61885
|
groupId: external_exports.string().describe("the group's id (from jefri_groups or jefri_inbox)"),
|
|
61828
|
-
...fileSendSchema(!!ctx.local)
|
|
61886
|
+
...fileSendSchema(!!ctx.local, hostFileUpload)
|
|
61829
61887
|
}
|
|
61830
61888
|
},
|
|
61831
61889
|
async ({ groupId, path: path39, caption, dataUrl, fileUrl, fileName }, extra) => withClient(async (c2) => {
|
|
@@ -61858,7 +61916,7 @@ Or just upload ${fname} from the agent's page in the web app.`
|
|
|
61858
61916
|
return fail(`Couldn't fetch that URL: ${e2?.message ?? e2}`);
|
|
61859
61917
|
}
|
|
61860
61918
|
} else {
|
|
61861
|
-
return fail(missingFileSource(!!ctx.local));
|
|
61919
|
+
return fail(missingFileSource(!!ctx.local, hostFileUpload));
|
|
61862
61920
|
}
|
|
61863
61921
|
const rawBytes = inlineBytes ?? Buffer.from(url.split(",")[1] ?? "", "base64");
|
|
61864
61922
|
if (rawBytes.length > MAX_UPLOAD_BYTES)
|
|
@@ -62678,7 +62736,7 @@ function attachInbox(c2, inbox2, self, onIncoming) {
|
|
|
62678
62736
|
c2.off("file_received", capture);
|
|
62679
62737
|
};
|
|
62680
62738
|
}
|
|
62681
|
-
var isStateless, crossBasename, DOWNLOAD_DIR_SH, downloadDirAbs, ensureDownloadDir, safeSaveName, shPathArg, psq, psPathArg, isTextyFile, DATAURL_INLINE_MAX, optionalFileString, REMOTE_FILE_GUIDANCE, fileSendSchema, MINT_LINK_DEADLINE_MS, forwardTip, MIME, mimeOf, MAX_UPLOAD_BYTES, posIntEnv, MAX_REMOTE_URL_BYTES, MAX_CONCURRENT_REMOTE_DOWNLOADS, REMOTE_DOWNLOAD_DEADLINE_MS, activeRemoteDownloads, MB, WS_SAFE_FILE_BYTES, extFromMime, shq, CAP, PROFILE_CAPS, LOCAL_CAPS, REMOTE_CAPS, LOCAL_PROFILES, PROFILE_LOOKUP_TIMEOUT_MS, CREDENTIAL_PROFILE_TTL_MS, CREDENTIAL_PROFILE_GRACE_MS, credentialProfiles, credentialEpochs, cacheKey, nextCredentialEpoch, epochOf, onAuthorityRejected, upgradeNotice, upgradeShownAt, UPGRADE_REPEAT_MS, ok3, okWithData, fail, AMBIGUOUS_OUTCOME, OUTCOME_META_KEY, AMBIGUOUS_CAVEAT, failAmbiguous, definiteHttpReject, okConnector, INLINE_IMAGE_MIMES, IMAGE_INLINE_MAX, IMAGE_MAX_PIXELS, IMAGE_MAX_FRAMES, IMAGE_RETAINED_FACTOR, retainedCost, IMAGE_BUDGET_MAX, imageBudgetInUse, IMAGE_HOLD_MAX_MS, dropBody, looksLikeImage, okWithImage, WATERMARK_FILE, inboxWatermark, STANDBY_ACKED_FILE, standbyAcked, pendingStandbyAck, STANDBY_VERIFY_MS;
|
|
62739
|
+
var isStateless, crossBasename, DOWNLOAD_DIR_SH, downloadDirAbs, ensureDownloadDir, safeSaveName, shPathArg, psq, psPathArg, isTextyFile, DATAURL_INLINE_MAX, optionalFileString, REMOTE_FILE_GUIDANCE, HOST_FILE_GUIDANCE, fileSendSchema, MINT_LINK_DEADLINE_MS, forwardTip, MIME, mimeOf, MAX_UPLOAD_BYTES, posIntEnv, MAX_REMOTE_URL_BYTES, MAX_CONCURRENT_REMOTE_DOWNLOADS, REMOTE_DOWNLOAD_DEADLINE_MS, activeRemoteDownloads, MB, WS_SAFE_FILE_BYTES, extFromMime, shq, CAP, PROFILE_CAPS, LOCAL_CAPS, REMOTE_CAPS, LOCAL_PROFILES, PROFILE_LOOKUP_TIMEOUT_MS, CREDENTIAL_PROFILE_TTL_MS, CREDENTIAL_PROFILE_GRACE_MS, credentialProfiles, credentialEpochs, cacheKey, nextCredentialEpoch, epochOf, onAuthorityRejected, upgradeNotice, upgradeShownAt, UPGRADE_REPEAT_MS, ok3, okWithData, fail, AMBIGUOUS_OUTCOME, OUTCOME_META_KEY, AMBIGUOUS_CAVEAT, failAmbiguous, definiteHttpReject, okConnector, INLINE_IMAGE_MIMES, IMAGE_INLINE_MAX, IMAGE_MAX_PIXELS, IMAGE_MAX_FRAMES, IMAGE_RETAINED_FACTOR, retainedCost, IMAGE_BUDGET_MAX, imageBudgetInUse, IMAGE_HOLD_MAX_MS, dropBody, looksLikeImage, okWithImage, WATERMARK_FILE, inboxWatermark, STANDBY_ACKED_FILE, standbyAcked, pendingStandbyAck, STANDBY_VERIFY_MS;
|
|
62682
62740
|
var init_tools = __esm({
|
|
62683
62741
|
"src/tools.ts"() {
|
|
62684
62742
|
"use strict";
|
|
@@ -62727,8 +62785,9 @@ var init_tools = __esm({
|
|
|
62727
62785
|
DATAURL_INLINE_MAX = 256 * 1024;
|
|
62728
62786
|
optionalFileString = external_exports.string().nullish().transform((value) => value ?? void 0);
|
|
62729
62787
|
REMOTE_FILE_GUIDANCE = "REMOTE connector (ChatGPT, Claude, or another cloud assistant): choose exactly ONE file source. (1) File in YOUR OWN sandbox: check its decoded byte size first. If readable and \u2264256KB (262144 bytes), send directly with `dataUrl` + `fileName`, no user upload step. This includes PDFs, CSVs, code, and small images. Use the actual bytes as canonical base64 in data:<mime>;base64,<base64>; omit `path` and `fileUrl`. (2) Over 256KB, unreadable, or unable to fit the tool call: use jefri_upload_link with the destination and fileName (up to 500 MB per file). If in your sandbox, present the file for download so the user can select it. Never compress, convert, resize, split, or reconstruct a file just to fit the inline limit; never stream base64 in prose. (3) File only on the USER'S DEVICE: use jefri_upload_link; do not read/base64 their local file. (4) An existing public http(s) URL \u2192 `fileUrl` (\u226425MB). Never invent a URL or pass sandbox:/file: URLs. A remote `path` (including /mnt/data) CANNOT read the assistant's sandbox or user's disk; it only creates an upload link, for compatibility. Link creation is NOT file delivery. Omit unused arguments (null is also accepted). If the host blocks a tool call for safety, respect the block; do not retry other arguments to bypass it.";
|
|
62730
|
-
|
|
62731
|
-
|
|
62788
|
+
HOST_FILE_GUIDANCE = "Grok Bot/Muse REMOTE connector: choose by where the original file bytes live and the tools actually available. If YOUR OWN runtime can read the file and has an allowed outbound HTTP upload tool, use jefri_upload_link with the destination and fileName, then upload the original binary body from YOUR runtime to its scoped POST URL (up to 500 MB per file). Do not ask the user to re-upload a file you can upload yourself. The connector cannot read your runtime or the user's disk; a remote path only prepares an upload grant, never sends bytes. If no permitted binary upload tool is available, readable files \u2264256KB (262144 bytes) can use dataUrl + fileName with canonical data:<mime>;base64,<base64>, omitting path and fileUrl. Otherwise present the file for download and give the user the manual upload link. Files only on the USER'S DEVICE also need that manual upload. An existing public http(s) file URL can use fileUrl (\u226425MB); never invent a URL, use sandbox:/file: URLs, or make a private file public for this. Choose exactly ONE source; omit unused arguments (null is also accepted). Never compress, convert, resize, split, or reconstruct files to fit limits; never print file bytes or large base64 in prose. Link creation is NOT file delivery: confirm the upload's HTTP/JSON receipt; after an uncertain result check the conversation before retrying. Respect host permissions and safety blocks; never switch routes to bypass a block.";
|
|
62789
|
+
fileSendSchema = (local, hostUpload = false) => ({
|
|
62790
|
+
path: optionalFileString.describe(local ? "Local file path to read and send directly, e.g. ~/Downloads/report.pdf. Do not combine with dataUrl or fileUrl." : hostUpload ? "Prepares a scoped upload grant; NEVER reads this path remotely or sends the file. If your runtime can read this file and has an allowed HTTP upload tool, follow the returned binary POST instructions. Otherwise use the manual link." : "Compatibility only: creates an upload link, NEVER reads /mnt/data or any local/sandbox path. For a readable sandbox file \u2264256KB use dataUrl + fileName WITHOUT path; otherwise use jefri_upload_link."),
|
|
62732
62791
|
caption: optionalFileString.describe("optional text caption"),
|
|
62733
62792
|
dataUrl: optionalFileString.describe(local ? "Small content generated in-conversation, max 256KB decoded. Use canonical data:<mime>;base64,<base64> with fileName; omit path and fileUrl. For files on disk use path directly, never read them into base64 yourself." : "Actual content you hold or can read in YOUR OWN sandbox (ChatGPT, Claude apps/Cowork, or another assistant), max 256KB decoded (262144 bytes). Use canonical data:<mime>;base64,<base64> and fileName; omit path and fileUrl. Never base64 a file on the USER machine. Never reconstruct missing bytes or inline files over the cap."),
|
|
62734
62793
|
fileUrl: optionalFileString.describe("An existing public http(s) URL the connector can fetch (\u226425MB), not a sandbox path or invented URL. Do not combine with path or dataUrl."),
|