@bike4mind/cli 2.0.0 → 2.1.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/README.md +1 -1
- package/dist/{AgentHistoryStore-DIMzvf-y.mjs → AgentHistoryStore-DdYKfx7C.mjs} +145 -95
- package/dist/{ApiClient-BTI0-m0g.mjs → ApiClient-dIB3YQc2.mjs} +2 -2
- package/dist/{ConfigStore-DIs-K9oD.mjs → ConfigStore-BygzEI6H.mjs} +2756 -1316
- package/dist/{WorkItemsClient-Cow6nXx7.mjs → WorkItemsClient-ClBC49y0.mjs} +52 -8
- package/dist/{buildAgent-DOvEp92a.mjs → buildAgent-B7cMwRAy.mjs} +33 -2
- package/dist/commands/acpCommand.mjs +4 -4
- package/dist/commands/apiCommand.mjs +1 -1
- package/dist/commands/doctorCommand.mjs +1 -1
- package/dist/commands/envCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +4 -4
- package/dist/commands/mcpCommand.mjs +3 -3
- package/dist/commands/pluginCommand.mjs +1 -1
- package/dist/commands/updateCommand.mjs +1 -1
- package/dist/index.mjs +75 -23
- package/dist/{package-Dl_F2Fah.mjs → package-CRNnNXxq.mjs} +1 -1
- package/dist/{serve-Y2zMbfGs.mjs → serve-DM7ld3-o.mjs} +452 -25
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -395,7 +395,7 @@ You can also run any MCP server via npx or custom executables:
|
|
|
395
395
|
|
|
396
396
|
#### Serve Bike4Mind itself as an MCP server (`b4m mcp serve`)
|
|
397
397
|
|
|
398
|
-
The reverse of adding a server: `b4m mcp serve` exposes your Bike4Mind backend to any MCP client (Claude Desktop, editors, other agents). It advertises
|
|
398
|
+
The reverse of adding a server: `b4m mcp serve` exposes your Bike4Mind backend to any MCP client (Claude Desktop, editors, other agents). It advertises seventeen tools (`list_notebooks`, `get_notebook`, `create_notebook`, `rename_notebook`, `clone_notebook`, `delete_notebook`, `list_projects`, `get_project`, `create_project`, `send_message`, `search_knowledge_base`, `list_lakes`, `list_files`, `get_file`, `generate_sound_effect`, `text_to_speech`, `generate_image`) plus four resource templates (`b4m://notebook/{id}`, `b4m://file/{id}`, `b4m://project/{id}`, `b4m://artifact/{id}`), each listable and readable as JSON, and `b4m://agent-quest` - the credential-free manifest for The Open Door, the onboarding quest for agents.
|
|
399
399
|
|
|
400
400
|
To get answers grounded in a data lake, call `list_lakes`, then `create_notebook { dataLakeId }` with a lake's id, then `send_message { notebookId }` on that notebook: the result carries the `reply` plus the `citables` it was grounded in. `list_lakes` needs a key with `datalake:read` (and data lakes enabled on the instance).
|
|
401
401
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { A as ChatModels, B as NotFoundError, E as BedrockEmbeddingModel, H as OpenAIEmbeddingModel, I as LOCATION_MAP_LANGUAGE, J as SupportedFabFileMimeTypes, K as SEARCH_RESULT_CARDS_LANGUAGE, Mt as APP_NAME, Nt as WEBSITE_URL, R as McpServerName, V as OllamaEmbeddingModel, W as PermissionDeniedError, X as WORK_ITEM_STATUSES, Y as VoyageAIEmbeddingModel, a as htmlErrorTitle, c as loadContextFiles, ct as isRetryableError, d as isReadOnlyTool, dt as obfuscateApiKey, gt as splitEquals, ht as signImageUrl, i as isValidSessionId, it as googleMapsSearchUrl, l as canTrustTool, mt as secureParameters, n as logger, nt as getMcpProviderMetadata, o as htmlFirstH1, r as SESSION_ID_PATTERN, st as isPlaceholderImageSigningSecret, u as getToolCategory, w as ApiKeyType, wt as withRetry } from "./ConfigStore-BygzEI6H.mjs";
|
|
3
3
|
import { n as isPathAllowed, t as assertPathAllowed } from "./pathValidation-BRqf4HFX-CHwtwp3O.mjs";
|
|
4
4
|
import { n as isTerminalShellStatus, t as getShellSessionManager } from "./ShellSessionManager-6o8KZzl1-vrbPAUTq.mjs";
|
|
5
5
|
import { execFile, execFileSync, spawn } from "child_process";
|
|
@@ -2210,7 +2210,64 @@ const webFetchTool = {
|
|
|
2210
2210
|
})
|
|
2211
2211
|
};
|
|
2212
2212
|
//#endregion
|
|
2213
|
-
//#region ../../b4m-core/services/dist/
|
|
2213
|
+
//#region ../../b4m-core/services/dist/prompts-Df-9fQuz.mjs
|
|
2214
|
+
/**
|
|
2215
|
+
* Teaches the `b4m_cards` fence, appended to a web_search result ONLY when that search actually
|
|
2216
|
+
* returned images (see shouldIncludeImages). Delivering it with the results rather than in the system
|
|
2217
|
+
* prompt costs nothing on the turns that will never use it, and arrives exactly when it is actionable.
|
|
2218
|
+
*
|
|
2219
|
+
* The field names must stay in sync with the parser in
|
|
2220
|
+
* apps/client/app/components/Session/parseSearchResultCards.ts; the fence language is shared.
|
|
2221
|
+
*/
|
|
2222
|
+
const WEB_SEARCH_CARDS_PROMPT = [
|
|
2223
|
+
"The results above include images, so illustrate your answer - do not wait to be asked. When you name specific",
|
|
2224
|
+
`things the user would want to SEE, emit a \`\`\`${SEARCH_RESULT_CARDS_LANGUAGE} fenced block inline in your reply, placed`,
|
|
2225
|
+
"exactly where the pictures belong - right after the sentence that introduces them, not at the end.",
|
|
2226
|
+
"The block is a single JSON object:",
|
|
2227
|
+
"",
|
|
2228
|
+
`\`\`\`${SEARCH_RESULT_CARDS_LANGUAGE}`,
|
|
2229
|
+
"{\"cards\":[{\"name\":\"Orient Bambino\",\"note\":\"Your own description of this thing, in your voice.\",",
|
|
2230
|
+
"\"meta\":\"~$200\",\"url\":\"https://orientwatch.co/bambino\",\"images\":[",
|
|
2231
|
+
"{\"url\":\"https://example.com/a.jpg\",\"source\":\"orientwatch.co\"},",
|
|
2232
|
+
"{\"url\":\"https://example.com/b.jpg\",\"source\":\"jomashop\"}]}]}",
|
|
2233
|
+
"```",
|
|
2234
|
+
"",
|
|
2235
|
+
"Rules: `name` and at least one `images` entry are required. Every image `url` must be copied verbatim",
|
|
2236
|
+
"from an `image:` or Images line above, never invented or guessed, and `source` is that entry's own",
|
|
2237
|
+
"`source`/hostname, so each picture is attributed. Prefer the \"Images found for this search\" pool:",
|
|
2238
|
+
"those carry their own page and publisher. `note` is YOUR prose about the thing, not the",
|
|
2239
|
+
"search snippet. `meta` is a short footer such as a price or key spec. `url` is where the card links.",
|
|
2240
|
+
"Two to six cards is the useful range. Keep writing normally around the block - it replaces neither",
|
|
2241
|
+
"your explanation nor your citations. Omit the block only if the results genuinely have no images",
|
|
2242
|
+
"worth showing; never tell the user you could show pictures if they asked - just show them."
|
|
2243
|
+
].join("\n");
|
|
2244
|
+
/**
|
|
2245
|
+
* Teaches the `b4m_map` fence, appended to a web_search result ONLY when that search returned places
|
|
2246
|
+
* with provider coordinates (see shouldIncludePlaces). The fence names places by id alone, so the
|
|
2247
|
+
* model never writes a coordinate - the client resolves each id against the stored place citables.
|
|
2248
|
+
*
|
|
2249
|
+
* The field names must stay in sync with parseLocationMapFence in
|
|
2250
|
+
* b4m-core/common/src/constants/locationMap.ts.
|
|
2251
|
+
*/
|
|
2252
|
+
const WEB_SEARCH_MAP_PROMPT = [
|
|
2253
|
+
"The results above include places with map locations, so show them on a map - do not wait to be asked.",
|
|
2254
|
+
`Emit one \`\`\`${LOCATION_MAP_LANGUAGE} fenced block inline in your reply, placed where the map belongs - right`,
|
|
2255
|
+
"after the sentence that introduces the places. The block is a single JSON object:",
|
|
2256
|
+
"",
|
|
2257
|
+
`\`\`\`${LOCATION_MAP_LANGUAGE}`,
|
|
2258
|
+
"{\"anchor\":{\"id\":\"<anchor id>\",\"name\":\"citizenM Copenhagen\",\"label\":\"Your hotel\"},",
|
|
2259
|
+
"\"places\":[{\"id\":\"<place id>\",\"name\":\"Barr\",\"note\":\"Your own one-line take on this place.\"}]}",
|
|
2260
|
+
"```",
|
|
2261
|
+
"",
|
|
2262
|
+
"Rules: every `id` must be copied verbatim from an `id:` line above - never invent one, and never write",
|
|
2263
|
+
"coordinates; the map locates each place from its id. `name` is the place name as listed. `note` is",
|
|
2264
|
+
"YOUR short line about why it fits the request. Include `anchor` only when an \"Anchor location\" is listed",
|
|
2265
|
+
"above, with a short `label` for what it is to the user (\"Your hotel\", \"Your office\"). List the places",
|
|
2266
|
+
"worth recommending, three to ten. Keep writing normally around the block - it does not replace your",
|
|
2267
|
+
"explanation or citations. Omit it only if none of the places actually answer the question."
|
|
2268
|
+
].join("\n");
|
|
2269
|
+
//#endregion
|
|
2270
|
+
//#region ../../b4m-core/services/dist/websearch-BpzP6-Zz.mjs
|
|
2214
2271
|
/**
|
|
2215
2272
|
* Per-process cache for web-search results. Keyed on normalized query + result count, with a
|
|
2216
2273
|
* short TTL so repeated/similar searches within a turn or across back-to-back turns reuse the
|
|
@@ -2788,61 +2845,6 @@ async function resolveWebSearchProviders(adapters) {
|
|
|
2788
2845
|
if (lead.name === "serpapi") return [lead, settings.searxngUrl ? createSearxngProvider(settings.searxngUrl) : null];
|
|
2789
2846
|
return [lead, settings.serperKey ? createSerpApiProvider(adapters) : null];
|
|
2790
2847
|
}
|
|
2791
|
-
/**
|
|
2792
|
-
* Teaches the `b4m_cards` fence, appended to a web_search result ONLY when that search actually
|
|
2793
|
-
* returned images (see shouldIncludeImages). Delivering it with the results rather than in the system
|
|
2794
|
-
* prompt costs nothing on the turns that will never use it, and arrives exactly when it is actionable.
|
|
2795
|
-
*
|
|
2796
|
-
* The field names must stay in sync with the parser in
|
|
2797
|
-
* apps/client/app/components/Session/parseSearchResultCards.ts; the fence language is shared.
|
|
2798
|
-
*/
|
|
2799
|
-
const WEB_SEARCH_CARDS_PROMPT = [
|
|
2800
|
-
"The results above include images, so illustrate your answer - do not wait to be asked. When you name specific",
|
|
2801
|
-
`things the user would want to SEE, emit a \`\`\`${SEARCH_RESULT_CARDS_LANGUAGE} fenced block inline in your reply, placed`,
|
|
2802
|
-
"exactly where the pictures belong - right after the sentence that introduces them, not at the end.",
|
|
2803
|
-
"The block is a single JSON object:",
|
|
2804
|
-
"",
|
|
2805
|
-
`\`\`\`${SEARCH_RESULT_CARDS_LANGUAGE}`,
|
|
2806
|
-
"{\"cards\":[{\"name\":\"Orient Bambino\",\"note\":\"Your own description of this thing, in your voice.\",",
|
|
2807
|
-
"\"meta\":\"~$200\",\"url\":\"https://orientwatch.co/bambino\",\"images\":[",
|
|
2808
|
-
"{\"url\":\"https://example.com/a.jpg\",\"source\":\"orientwatch.co\"},",
|
|
2809
|
-
"{\"url\":\"https://example.com/b.jpg\",\"source\":\"jomashop\"}]}]}",
|
|
2810
|
-
"```",
|
|
2811
|
-
"",
|
|
2812
|
-
"Rules: `name` and at least one `images` entry are required. Every image `url` must be copied verbatim",
|
|
2813
|
-
"from an `image:` or Images line above, never invented or guessed, and `source` is that entry's own",
|
|
2814
|
-
"`source`/hostname, so each picture is attributed. Prefer the \"Images found for this search\" pool:",
|
|
2815
|
-
"those carry their own page and publisher. `note` is YOUR prose about the thing, not the",
|
|
2816
|
-
"search snippet. `meta` is a short footer such as a price or key spec. `url` is where the card links.",
|
|
2817
|
-
"Two to six cards is the useful range. Keep writing normally around the block - it replaces neither",
|
|
2818
|
-
"your explanation nor your citations. Omit the block only if the results genuinely have no images",
|
|
2819
|
-
"worth showing; never tell the user you could show pictures if they asked - just show them."
|
|
2820
|
-
].join("\n");
|
|
2821
|
-
/**
|
|
2822
|
-
* Teaches the `b4m_map` fence, appended to a web_search result ONLY when that search returned places
|
|
2823
|
-
* with provider coordinates (see shouldIncludePlaces). The fence names places by id alone, so the
|
|
2824
|
-
* model never writes a coordinate - the client resolves each id against the stored place citables.
|
|
2825
|
-
*
|
|
2826
|
-
* The field names must stay in sync with parseLocationMapFence in
|
|
2827
|
-
* b4m-core/common/src/constants/locationMap.ts.
|
|
2828
|
-
*/
|
|
2829
|
-
const WEB_SEARCH_MAP_PROMPT = [
|
|
2830
|
-
"The results above include places with map locations, so show them on a map - do not wait to be asked.",
|
|
2831
|
-
`Emit one \`\`\`${LOCATION_MAP_LANGUAGE} fenced block inline in your reply, placed where the map belongs - right`,
|
|
2832
|
-
"after the sentence that introduces the places. The block is a single JSON object:",
|
|
2833
|
-
"",
|
|
2834
|
-
`\`\`\`${LOCATION_MAP_LANGUAGE}`,
|
|
2835
|
-
"{\"anchor\":{\"id\":\"<anchor id>\",\"name\":\"citizenM Copenhagen\",\"label\":\"Your hotel\"},",
|
|
2836
|
-
"\"places\":[{\"id\":\"<place id>\",\"name\":\"Barr\",\"note\":\"Your own one-line take on this place.\"}]}",
|
|
2837
|
-
"```",
|
|
2838
|
-
"",
|
|
2839
|
-
"Rules: every `id` must be copied verbatim from an `id:` line above - never invent one, and never write",
|
|
2840
|
-
"coordinates; the map locates each place from its id. `name` is the place name as listed. `note` is",
|
|
2841
|
-
"YOUR short line about why it fits the request. Include `anchor` only when an \"Anchor location\" is listed",
|
|
2842
|
-
"above, with a short `label` for what it is to the user (\"Your hotel\", \"Your office\"). List the places",
|
|
2843
|
-
"worth recommending, three to ten. Keep writing normally around the block - it does not replace your",
|
|
2844
|
-
"explanation or citations. Omit it only if none of the places actually answer the question."
|
|
2845
|
-
].join("\n");
|
|
2846
2848
|
function safeHostname(url) {
|
|
2847
2849
|
try {
|
|
2848
2850
|
return new URL(url).hostname;
|
|
@@ -3340,7 +3342,7 @@ async function getModelForWrite(user, modelId, deps) {
|
|
|
3340
3342
|
return isModelOwner(model, user) ? model : null;
|
|
3341
3343
|
}
|
|
3342
3344
|
//#endregion
|
|
3343
|
-
//#region ../../b4m-core/services/dist/toolGenerators-
|
|
3345
|
+
//#region ../../b4m-core/services/dist/toolGenerators-DT6OlvUe.mjs
|
|
3344
3346
|
const diceRoll = async (parameters) => {
|
|
3345
3347
|
if (!parameters?.sides || !parameters?.times) throw new Error("Tool dice roll: Missing required parameters");
|
|
3346
3348
|
return sum(times(parameters.times, () => random(1, parameters.sides))).toString();
|
|
@@ -3930,27 +3932,6 @@ const promptEnhancementTool = {
|
|
|
3930
3932
|
}
|
|
3931
3933
|
})
|
|
3932
3934
|
};
|
|
3933
|
-
/**
|
|
3934
|
-
* Is this value shaped like something Mongoose can cast to an `_id`?
|
|
3935
|
-
*
|
|
3936
|
-
* Tool arguments are composed by the model out of conversation text and reach us as unvalidated
|
|
3937
|
-
* JSON, so an id parameter routinely holds something that is not an id at all - a filename token,
|
|
3938
|
-
* an arXiv number, a bare integer. Mongoose casts `_id` and throws a CastError on those, which a
|
|
3939
|
-
* generic catch upstream then reports as an outage rather than the bad argument it is (#2530).
|
|
3940
|
-
* Call this before handing a model-supplied id to `findById` and answer a false the same way the
|
|
3941
|
-
* surface answers a genuinely missing row.
|
|
3942
|
-
*
|
|
3943
|
-
* `isObjectIdOrHexString`, not `isValidObjectId`: the latter also accepts a number and casts it to
|
|
3944
|
-
* a fabricated id, and a model emitting `{"file_id": 12}` gives us exactly that despite the
|
|
3945
|
-
* `string` type. Same choice, same reason, as `usableObjectIds` in @bike4mind/db-core, which is
|
|
3946
|
-
* the array-shaped version of this check.
|
|
3947
|
-
*
|
|
3948
|
-
* NOT usable for artifact ids (`artifact_<...>`), which are matched on a string `id` field rather
|
|
3949
|
-
* than `_id` - see `createArtifactId` in @bike4mind/common.
|
|
3950
|
-
*/
|
|
3951
|
-
function isObjectIdShaped$1(id) {
|
|
3952
|
-
return isObjectIdOrHexString(id);
|
|
3953
|
-
}
|
|
3954
3935
|
let _showUserQuestion = null;
|
|
3955
3936
|
/**
|
|
3956
3937
|
* Inject the CLI callback that displays the question UI.
|
|
@@ -4070,7 +4051,7 @@ const askUserQuestionTool = {
|
|
|
4070
4051
|
* re-export them without pulling the full tool graph. `index.ts` re-exports them
|
|
4071
4052
|
* so the server barrel's public API is unchanged.
|
|
4072
4053
|
*/
|
|
4073
|
-
const generateTools = (userId, user, logger, { db, retrievalFilter, kbScope, inlinedAttachmentIds, fullyInlinedAttachmentIds, suppressLakeArms, sessionRetrievalTags, sessionReaderConsentDatalakeTags, sessionLakeScopeExplicit, sessionPreauthorizedLakeIds, organizationId, apiKeyId, questId, getAbortSignal }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, imageProcessorLambdaName, tools, allowedDirectories, entitlementKeys = [], sessionId, codeMinifier, availableModels, onToolLlmUsage) => {
|
|
4054
|
+
const generateTools = (userId, user, logger, { db, retrievalFilter, kbScope, inlinedAttachmentIds, attachedFileIds, fullyInlinedAttachmentIds, suppressLakeArms, sessionRetrievalTags, sessionReaderConsentDatalakeTags, sessionLakeScopeExplicit, sessionIncludeLibraryFiles, sessionPreauthorizedLakeIds, organizationId, apiKeyId, questId, getAbortSignal }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model, imageProcessorLambdaName, tools, allowedDirectories, entitlementKeys = [], sessionId, codeMinifier, availableModels, onToolLlmUsage) => {
|
|
4074
4055
|
const context = {
|
|
4075
4056
|
userId,
|
|
4076
4057
|
user,
|
|
@@ -4091,11 +4072,13 @@ const generateTools = (userId, user, logger, { db, retrievalFilter, kbScope, inl
|
|
|
4091
4072
|
retrievalFilter,
|
|
4092
4073
|
kbScope,
|
|
4093
4074
|
inlinedAttachmentIds,
|
|
4075
|
+
attachedFileIds,
|
|
4094
4076
|
fullyInlinedAttachmentIds,
|
|
4095
4077
|
suppressLakeArms,
|
|
4096
4078
|
sessionRetrievalTags,
|
|
4097
4079
|
sessionReaderConsentDatalakeTags,
|
|
4098
4080
|
sessionLakeScopeExplicit,
|
|
4081
|
+
sessionIncludeLibraryFiles,
|
|
4099
4082
|
sessionPreauthorizedLakeIds,
|
|
4100
4083
|
organizationId,
|
|
4101
4084
|
apiKeyId,
|
|
@@ -4116,16 +4099,34 @@ const generateTools = (userId, user, logger, { db, retrievalFilter, kbScope, inl
|
|
|
4116
4099
|
}, {});
|
|
4117
4100
|
};
|
|
4118
4101
|
/**
|
|
4102
|
+
* MCP tool args only the confirm-button handlers may set. Those handlers call the MCP host
|
|
4103
|
+
* directly, never through these generators, so a model-originated call carrying one is forged.
|
|
4104
|
+
* `_executeFromButton` mirrors the button-only keys of `confirmationParams` in
|
|
4105
|
+
* b4m-core/mcp/src/shared/schemas.ts (pinned by github/__tests__/helpers/schemas.test.ts there);
|
|
4106
|
+
* `_confirmToken` is the preview token tools return.
|
|
4107
|
+
*/
|
|
4108
|
+
const SERVER_ONLY_MCP_ARG_KEYS = ["_executeFromButton", "_confirmToken"];
|
|
4109
|
+
const isPlainObject$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4110
|
+
function stripServerOnlyArgs(args) {
|
|
4111
|
+
if (!isPlainObject$1(args)) return args;
|
|
4112
|
+
return Object.fromEntries(Object.entries(args).filter(([key]) => !SERVER_ONLY_MCP_ARG_KEYS.includes(key)));
|
|
4113
|
+
}
|
|
4114
|
+
/**
|
|
4119
4115
|
* Normalize MCP tool parameters for OpenAI compatibility.
|
|
4120
4116
|
* OpenAI requires 'properties' on object schemas - MCP tools like current_user
|
|
4121
4117
|
* return { type: 'object' } without it, causing 400 errors.
|
|
4122
4118
|
*/
|
|
4123
4119
|
function normalizeToolParameters(rest) {
|
|
4124
4120
|
const rawParameters = rest?.input_schema ?? rest?.inputSchema ?? rest?.parameters;
|
|
4125
|
-
if (rawParameters
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4121
|
+
if (isPlainObject$1(rawParameters)) {
|
|
4122
|
+
const properties = isPlainObject$1(rawParameters.properties) ? stripServerOnlyArgs(rawParameters.properties) : rawParameters.properties ?? {};
|
|
4123
|
+
const required = Array.isArray(rawParameters.required) ? rawParameters.required.filter((key) => typeof key !== "string" || !SERVER_ONLY_MCP_ARG_KEYS.includes(key)) : rawParameters.required;
|
|
4124
|
+
return {
|
|
4125
|
+
...rawParameters,
|
|
4126
|
+
properties,
|
|
4127
|
+
...required !== void 0 ? { required } : {}
|
|
4128
|
+
};
|
|
4129
|
+
}
|
|
4129
4130
|
return {
|
|
4130
4131
|
type: "object",
|
|
4131
4132
|
properties: {},
|
|
@@ -4158,7 +4159,7 @@ const generateMcpTools = async (mcpData) => {
|
|
|
4158
4159
|
toolFn: async (args) => {
|
|
4159
4160
|
Logger.debug(`Calling ${originalToolName} tool via ${mcpData.serverName}`, args);
|
|
4160
4161
|
try {
|
|
4161
|
-
const toolResult = await mcpData.callTool(originalToolName, args);
|
|
4162
|
+
const toolResult = await mcpData.callTool(originalToolName, stripServerOnlyArgs(args));
|
|
4162
4163
|
const contentBlocks = toolResult?.content;
|
|
4163
4164
|
if (Array.isArray(contentBlocks) && contentBlocks.length > 0) {
|
|
4164
4165
|
const normalized = contentBlocks.map((entry) => {
|
|
@@ -4210,7 +4211,7 @@ const generateMcpToolsFromCache = (serverName, cachedTools, callTool) => {
|
|
|
4210
4211
|
toolFn: async (args) => {
|
|
4211
4212
|
Logger.debug(`Calling ${originalToolName} tool via ${serverName}`, args);
|
|
4212
4213
|
try {
|
|
4213
|
-
const toolResult = await callTool(originalToolName, args);
|
|
4214
|
+
const toolResult = await callTool(originalToolName, stripServerOnlyArgs(args));
|
|
4214
4215
|
const contentBlocks = toolResult?.content;
|
|
4215
4216
|
if (Array.isArray(contentBlocks) && contentBlocks.length > 0) {
|
|
4216
4217
|
const normalized = contentBlocks.map((entry) => {
|
|
@@ -4247,6 +4248,29 @@ const generateMcpToolsFromCache = (serverName, cachedTools, callTool) => {
|
|
|
4247
4248
|
return result;
|
|
4248
4249
|
};
|
|
4249
4250
|
//#endregion
|
|
4251
|
+
//#region ../../b4m-core/services/dist/objectId-DTad97PS.mjs
|
|
4252
|
+
/**
|
|
4253
|
+
* Is this value shaped like something Mongoose can cast to an `_id`?
|
|
4254
|
+
*
|
|
4255
|
+
* Tool arguments are composed by the model out of conversation text and reach us as unvalidated
|
|
4256
|
+
* JSON, so an id parameter routinely holds something that is not an id at all - a filename token,
|
|
4257
|
+
* an arXiv number, a bare integer. Mongoose casts `_id` and throws a CastError on those, which a
|
|
4258
|
+
* generic catch upstream then reports as an outage rather than the bad argument it is (#2530).
|
|
4259
|
+
* Call this before handing a model-supplied id to `findById` and answer a false the same way the
|
|
4260
|
+
* surface answers a genuinely missing row.
|
|
4261
|
+
*
|
|
4262
|
+
* `isObjectIdOrHexString`, not `isValidObjectId`: the latter also accepts a number and casts it to
|
|
4263
|
+
* a fabricated id, and a model emitting `{"file_id": 12}` gives us exactly that despite the
|
|
4264
|
+
* `string` type. Same choice, same reason, as `usableObjectIds` in @bike4mind/db-core, which is
|
|
4265
|
+
* the array-shaped version of this check.
|
|
4266
|
+
*
|
|
4267
|
+
* NOT usable for artifact ids (`artifact_<...>`), which are matched on a string `id` field rather
|
|
4268
|
+
* than `_id` - see `createArtifactId` in @bike4mind/common.
|
|
4269
|
+
*/
|
|
4270
|
+
function isObjectIdShaped(id) {
|
|
4271
|
+
return isObjectIdOrHexString(id);
|
|
4272
|
+
}
|
|
4273
|
+
//#endregion
|
|
4250
4274
|
//#region ../../b4m-core/services/dist/llm/tools/cliTools.mjs
|
|
4251
4275
|
/**
|
|
4252
4276
|
* Validated fuzzy fallback for `edit_local_file` string matching.
|
|
@@ -5636,7 +5660,7 @@ const latticeAddEntityTool = {
|
|
|
5636
5660
|
createdAt: /* @__PURE__ */ new Date(),
|
|
5637
5661
|
updatedAt: /* @__PURE__ */ new Date()
|
|
5638
5662
|
};
|
|
5639
|
-
if (context.db.latticeModels && modelId && isObjectIdShaped
|
|
5663
|
+
if (context.db.latticeModels && modelId && isObjectIdShaped(modelId)) try {
|
|
5640
5664
|
const model = await getModelForWrite({
|
|
5641
5665
|
id: context.userId,
|
|
5642
5666
|
organizationId: context.user?.organizationId
|
|
@@ -5780,7 +5804,7 @@ const latticeSetValueTool = {
|
|
|
5780
5804
|
else if (rawValue.toLowerCase() === "true") value = true;
|
|
5781
5805
|
else if (rawValue.toLowerCase() === "false") value = false;
|
|
5782
5806
|
const entityId = toEntityId(entityName);
|
|
5783
|
-
if (context.db.latticeModels && modelId && isObjectIdShaped
|
|
5807
|
+
if (context.db.latticeModels && modelId && isObjectIdShaped(modelId)) try {
|
|
5784
5808
|
const model = await getModelForWrite({
|
|
5785
5809
|
id: context.userId,
|
|
5786
5810
|
organizationId: context.user?.organizationId
|
|
@@ -5925,7 +5949,7 @@ const latticeCreateRuleTool = {
|
|
|
5925
5949
|
};
|
|
5926
5950
|
const outputEntityId = parsedRule.outputEntity.toLowerCase().replace(/\s+/g, "_");
|
|
5927
5951
|
let entityCreatedMessage = "";
|
|
5928
|
-
if (context.db.latticeModels && modelId && isObjectIdShaped
|
|
5952
|
+
if (context.db.latticeModels && modelId && isObjectIdShaped(modelId)) try {
|
|
5929
5953
|
const model = await getModelForWrite({
|
|
5930
5954
|
id: context.userId,
|
|
5931
5955
|
organizationId: context.user?.organizationId
|
|
@@ -9254,16 +9278,24 @@ function leadingProgram(args) {
|
|
|
9254
9278
|
return index < args.length ? programName(args[index]) : "";
|
|
9255
9279
|
}
|
|
9256
9280
|
/**
|
|
9257
|
-
* True if any
|
|
9258
|
-
* host shapes: SCSI/SATA (`sd[a-z]`), NVMe, Xen (`xvd[a-z]`,
|
|
9259
|
-
* AMIs), virtio (`vd[a-z]`), legacy IDE (`hd[a-z]`), device-mapper/LVM
|
|
9260
|
-
* `mapper/`), software RAID (`md N`), eMMC/SD (`mmcblkN`), loop, and macOS `diskN`.
|
|
9281
|
+
* True if any write redirection (`>`, `>>`, `>|`, `&>`, `&>>`, `<>`) targets a raw block
|
|
9282
|
+
* device. Covers the common host shapes: SCSI/SATA (`sd[a-z]`), NVMe, Xen (`xvd[a-z]`,
|
|
9283
|
+
* default on many EC2 AMIs), virtio (`vd[a-z]`), legacy IDE (`hd[a-z]`), device-mapper/LVM
|
|
9284
|
+
* (`dm-N`, `mapper/`), software RAID (`md N`), eMMC/SD (`mmcblkN`), loop, and macOS `diskN`.
|
|
9261
9285
|
*/
|
|
9262
9286
|
const BLOCK_DEVICE_RE = /^\/dev\/(sd[a-z]|nvme\d+n\d+|xvd[a-z]|vd[a-z]|hd[a-z]|dm-\d+|md\d+|mmcblk\d+|loop\d+|disk\d+|mapper\/)/;
|
|
9287
|
+
const WRITE_REDIRECT_OPS = /* @__PURE__ */ new Set([
|
|
9288
|
+
">",
|
|
9289
|
+
">>",
|
|
9290
|
+
">|",
|
|
9291
|
+
"&>",
|
|
9292
|
+
"&>>",
|
|
9293
|
+
"<>"
|
|
9294
|
+
]);
|
|
9263
9295
|
function writesToBlockDevice(tokens) {
|
|
9264
9296
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
9265
9297
|
const token = tokens[i];
|
|
9266
|
-
if (isOperator(token) && (token.op
|
|
9298
|
+
if (isOperator(token) && WRITE_REDIRECT_OPS.has(token.op)) {
|
|
9267
9299
|
let targetIndex = i + 1;
|
|
9268
9300
|
const following = tokens[targetIndex];
|
|
9269
9301
|
if (isOperator(following) && following.op === "|") targetIndex += 1;
|
|
@@ -15890,6 +15922,10 @@ var StreamAccumulator = class {
|
|
|
15890
15922
|
isEmpty() {
|
|
15891
15923
|
return this.accumulatedText.trim().length === 0 && this.toolsUsed.length === 0;
|
|
15892
15924
|
}
|
|
15925
|
+
/** The stop reason the stream reported, if any: its presence means the stream ended normally. */
|
|
15926
|
+
get finalStopReason() {
|
|
15927
|
+
return this.stopReason;
|
|
15928
|
+
}
|
|
15893
15929
|
get accumulatedLength() {
|
|
15894
15930
|
return this.accumulatedText.length;
|
|
15895
15931
|
}
|
|
@@ -15935,6 +15971,19 @@ var EmptyCompletionError = class extends Error {
|
|
|
15935
15971
|
this.name = "EmptyCompletionError";
|
|
15936
15972
|
}
|
|
15937
15973
|
};
|
|
15974
|
+
/**
|
|
15975
|
+
* Thrown when a stream finished normally (it carried a stop reason) but produced no reply,
|
|
15976
|
+
* e.g. a reasoning model that spent its whole output budget thinking: the server drops
|
|
15977
|
+
* reasoning text, so such a turn arrives blank. Not retried - the same request would
|
|
15978
|
+
* finish the same way and be billed again.
|
|
15979
|
+
*/
|
|
15980
|
+
var NoReplyCompletionError = class extends Error {
|
|
15981
|
+
constructor(stopReason) {
|
|
15982
|
+
super(stopReason === "max_tokens" ? "The model used its whole output limit on reasoning and produced no reply. Raise the max tokens setting or ask a narrower question." : `The model finished (stop reason: ${stopReason}) without producing a reply.`);
|
|
15983
|
+
this.stopReason = stopReason;
|
|
15984
|
+
this.name = "NoReplyCompletionError";
|
|
15985
|
+
}
|
|
15986
|
+
};
|
|
15938
15987
|
/** User-facing message when a transient network drop survives all retries. */
|
|
15939
15988
|
const TRANSIENT_EXHAUSTED_MESSAGE = "The connection dropped mid-response (likely a network timeout during a long thinking step). It was retried automatically but kept failing - type \"continue\" to resume.";
|
|
15940
15989
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -15979,12 +16028,13 @@ async function runCompletion(transport, req, callback, policy, signal) {
|
|
|
15979
16028
|
await accumulator.finalize(callback);
|
|
15980
16029
|
return;
|
|
15981
16030
|
}
|
|
16031
|
+
if (accumulator.finalStopReason) throw new NoReplyCompletionError(accumulator.finalStopReason);
|
|
15982
16032
|
lastError = new EmptyCompletionError();
|
|
15983
16033
|
logger.warn("[runCompletion] Stream produced no content; treating as retryable.");
|
|
15984
16034
|
} catch (error) {
|
|
15985
16035
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
15986
16036
|
if (signal?.aborted) return;
|
|
15987
|
-
if (!policy.isRetryable(lastError)) throw lastError;
|
|
16037
|
+
if (lastError instanceof NoReplyCompletionError || !policy.isRetryable(lastError)) throw lastError;
|
|
15988
16038
|
logger.warn(`[runCompletion] Transient stream failure (attempt ${attempt + 1}): ${lastError.message}`);
|
|
15989
16039
|
}
|
|
15990
16040
|
if (attempt < policy.maxRetries && backoffMs > 0) await sleep(backoffMs * (attempt + 1));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as logger, t as ConfigStore } from "./ConfigStore-
|
|
3
|
-
import { t as version } from "./package-
|
|
2
|
+
import { n as logger, t as ConfigStore } from "./ConfigStore-BygzEI6H.mjs";
|
|
3
|
+
import { t as version } from "./package-CRNnNXxq.mjs";
|
|
4
4
|
import axios, { isAxiosError } from "axios";
|
|
5
5
|
//#region src/auth/OAuthClient.ts
|
|
6
6
|
/**
|