@bike4mind/cli 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AgentHistoryStore-O2gKB5YG.mjs → AgentHistoryStore-BWko6txx.mjs} +6041 -5674
- package/dist/{ConfigStore-RHqyXcSS.mjs → ConfigStore-v0wgf0_j.mjs} +594 -306
- package/dist/{WorkItemsClient-cmw7WYBy.mjs → WorkItemsClient-BZL44N12.mjs} +1 -1
- package/dist/{buildAgent-C0jmyDL4.mjs → buildAgent-BZnmDQIr.mjs} +14 -206
- package/dist/commands/acpCommand.mjs +13 -9
- 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 +16 -15
- package/dist/commands/mcpCommand.mjs +3 -3
- package/dist/commands/pluginCommand.mjs +1 -1
- package/dist/commands/updateCommand.mjs +1 -1
- package/dist/{dist-B1bGf-yn.mjs → dist-5DDaX8tB.mjs} +2 -2
- package/dist/index.mjs +21 -15
- package/dist/{package-DsG8Tgvs.mjs → package-COr17yKT.mjs} +1 -1
- package/dist/{serve-CAfCQose.mjs → serve-D6Eu3oXv.mjs} +2 -2
- package/package.json +10 -10
|
@@ -250,6 +250,19 @@ const StorageLimitErrorInfoSchema = z$1.object({
|
|
|
250
250
|
requested_bytes: z$1.number().nonnegative().describe("Size of the refused upload, in bytes."),
|
|
251
251
|
owner: z$1.enum(["user", "org"]).describe("Whose limit was hit: `user` or `org`. Always `user` for `file_too_large`, a platform-wide cap.")
|
|
252
252
|
});
|
|
253
|
+
/**
|
|
254
|
+
* The refusal a caller gets when the limit belongs to someone else (an update-share editor growing
|
|
255
|
+
* another user's file): the code and whose limit, without that owner's byte counts.
|
|
256
|
+
*/
|
|
257
|
+
const RedactedStorageLimitErrorInfoSchema = StorageLimitErrorInfoSchema.pick({
|
|
258
|
+
errorCode: true,
|
|
259
|
+
owner: true
|
|
260
|
+
}).extend({
|
|
261
|
+
used_bytes: z$1.never().optional(),
|
|
262
|
+
limit_bytes: z$1.never().optional(),
|
|
263
|
+
requested_bytes: z$1.never().optional()
|
|
264
|
+
});
|
|
265
|
+
z$1.union([StorageLimitErrorInfoSchema, RedactedStorageLimitErrorInfoSchema]);
|
|
253
266
|
function isZodError(err) {
|
|
254
267
|
return Boolean(err && (err instanceof ZodError || err.name === "ZodError"));
|
|
255
268
|
}
|
|
@@ -3643,6 +3656,199 @@ z$1.enum([
|
|
|
3643
3656
|
"investigate_soon",
|
|
3644
3657
|
"immediate_action"
|
|
3645
3658
|
]);
|
|
3659
|
+
const CompletionReasoningEffortSchema = z$1.enum([
|
|
3660
|
+
"none",
|
|
3661
|
+
"minimal",
|
|
3662
|
+
"low",
|
|
3663
|
+
"medium",
|
|
3664
|
+
"high",
|
|
3665
|
+
"xhigh"
|
|
3666
|
+
]);
|
|
3667
|
+
/**
|
|
3668
|
+
* Tool schema matching ICompletionOptionTools.toolSchema. The Zod surface only
|
|
3669
|
+
* covers wire-format fields (toolFn is server-side). Replaces the historical
|
|
3670
|
+
* z.array(z.any()) so client-side bugs (typos in name, missing type) surface
|
|
3671
|
+
* at parse time instead of failing silently inside the backend.
|
|
3672
|
+
*/
|
|
3673
|
+
const CompletionCustomToolSchema = z$1.object({
|
|
3674
|
+
toolSchema: z$1.object({
|
|
3675
|
+
name: z$1.string(),
|
|
3676
|
+
description: z$1.string(),
|
|
3677
|
+
parameters: z$1.object({
|
|
3678
|
+
type: z$1.literal("object"),
|
|
3679
|
+
properties: z$1.record(z$1.string(), z$1.any()).optional(),
|
|
3680
|
+
additionalProperties: z$1.boolean().optional(),
|
|
3681
|
+
required: z$1.array(z$1.string()).optional()
|
|
3682
|
+
}).passthrough(),
|
|
3683
|
+
strict: z$1.boolean().optional(),
|
|
3684
|
+
defer_loading: z$1.boolean().optional().describe("Keep this definition out of the model context until the tool search server tool (or a `tool_reference`) loads it. Honoured only on models served by the direct Anthropic API whose `supports_tool_search` is true; on every other model (including Bedrock-served Claude, for now) it is ignored and the tool is sent as usual. At least one tool, or a `serverTool` entry, must stay non-deferred (`errorCode: \"all_tools_deferred\"`)."),
|
|
3685
|
+
cache_control: z$1.unknown().optional().describe("Accepted for validation only and not forwarded. Rejected on a deferred tool (`errorCode: \"deferred_tool_cache_control\"`).")
|
|
3686
|
+
}),
|
|
3687
|
+
_isMcpTool: z$1.boolean().optional()
|
|
3688
|
+
});
|
|
3689
|
+
/** Anthropic's tool search server tools; `name` is fixed by `type`. */
|
|
3690
|
+
const ToolSearchServerToolSchema = z$1.discriminatedUnion("type", [z$1.object({
|
|
3691
|
+
type: z$1.literal("tool_search_tool_regex_20251119"),
|
|
3692
|
+
name: z$1.literal("tool_search_tool_regex")
|
|
3693
|
+
}), z$1.object({
|
|
3694
|
+
type: z$1.literal("tool_search_tool_bm25_20251119"),
|
|
3695
|
+
name: z$1.literal("tool_search_tool_bm25")
|
|
3696
|
+
})]);
|
|
3697
|
+
const CompletionServerToolSchema = z$1.object({ serverTool: ToolSearchServerToolSchema.describe("A provider-run tool, sent to the provider as `{ type, name }`. Only the Anthropic tool search tools exist today, and only on models served by the direct Anthropic API whose `supports_tool_search` is true; any other model, Bedrock-served Claude included, is refused with `errorCode: \"tool_search_unsupported\"` rather than run without it.") });
|
|
3698
|
+
/** One `tools[]` entry: a caller-defined tool, or a provider-run server tool. */
|
|
3699
|
+
const CompletionToolSchema = z$1.union([CompletionCustomToolSchema, CompletionServerToolSchema]);
|
|
3700
|
+
/**
|
|
3701
|
+
* The `errorCode`s of the completions 400s for tool search requests (CONVENTIONS.md section 1).
|
|
3702
|
+
* Assigned in services/cliCompletionsToolSearch.ts.
|
|
3703
|
+
*/
|
|
3704
|
+
const COMPLETION_TOOL_ERROR_CODES = [
|
|
3705
|
+
"tool_search_unsupported",
|
|
3706
|
+
"all_tools_deferred",
|
|
3707
|
+
"deferred_tool_cache_control",
|
|
3708
|
+
"unknown_tool_reference"
|
|
3709
|
+
];
|
|
3710
|
+
/**
|
|
3711
|
+
* JSON Schema definition for structured output. The `schema` field is a full
|
|
3712
|
+
* JSON Schema document (we don't try to validate JSON Schema with Zod - that's
|
|
3713
|
+
* the caller's contract with the model). `strict: true` is the default, matching
|
|
3714
|
+
* OpenAI's strict-mode subset semantics.
|
|
3715
|
+
*/
|
|
3716
|
+
const ResponseFormatSchema = z$1.discriminatedUnion("type", [z$1.object({ type: z$1.literal("text") }), z$1.object({
|
|
3717
|
+
type: z$1.literal("json_schema"),
|
|
3718
|
+
json_schema: z$1.object({
|
|
3719
|
+
name: z$1.string(),
|
|
3720
|
+
description: z$1.string().optional(),
|
|
3721
|
+
schema: z$1.record(z$1.string(), z$1.any()),
|
|
3722
|
+
strict: z$1.boolean().optional().default(true)
|
|
3723
|
+
})
|
|
3724
|
+
})]);
|
|
3725
|
+
/**
|
|
3726
|
+
* Wire-format message for /api/ai/v1/completions. The optional `cache: true`
|
|
3727
|
+
* flag is honored by Anthropic (translated to `cache_control: { type: 'ephemeral' }`
|
|
3728
|
+
* with the appropriate beta header) and silently ignored by other providers.
|
|
3729
|
+
*/
|
|
3730
|
+
const CompletionMessageSchema = z$1.object({
|
|
3731
|
+
role: z$1.enum([
|
|
3732
|
+
"user",
|
|
3733
|
+
"assistant",
|
|
3734
|
+
"system"
|
|
3735
|
+
]),
|
|
3736
|
+
content: z$1.union([z$1.string(), z$1.array(z$1.any())]),
|
|
3737
|
+
cache: z$1.boolean().optional()
|
|
3738
|
+
});
|
|
3739
|
+
/**
|
|
3740
|
+
* Schema for CLI LLM completion requests
|
|
3741
|
+
* Shared between Next.js API route (dev) and Lambda function (production)
|
|
3742
|
+
*
|
|
3743
|
+
* `response_format`, `stream`, `tools`, `temperature`, and `max_tokens` are all
|
|
3744
|
+
* accepted at the top level (OpenAI-compatible, matching how every major LLM
|
|
3745
|
+
* SDK shapes a completion request) AND nested under `options` (legacy shape).
|
|
3746
|
+
* Use `normalizeCompletionRequest()` to collapse both surfaces into the
|
|
3747
|
+
* canonical `options.<field>` location before downstream consumption.
|
|
3748
|
+
*/
|
|
3749
|
+
const CompletionRequestSchema = z$1.object({
|
|
3750
|
+
model: z$1.string().max(256),
|
|
3751
|
+
messages: z$1.array(CompletionMessageSchema),
|
|
3752
|
+
response_format: ResponseFormatSchema.optional(),
|
|
3753
|
+
stream: z$1.boolean().optional(),
|
|
3754
|
+
tools: z$1.array(CompletionToolSchema).optional(),
|
|
3755
|
+
temperature: z$1.number().optional(),
|
|
3756
|
+
max_tokens: z$1.number().optional(),
|
|
3757
|
+
options: z$1.object({
|
|
3758
|
+
temperature: z$1.number().optional(),
|
|
3759
|
+
maxTokens: z$1.number().optional(),
|
|
3760
|
+
stream: z$1.boolean().optional(),
|
|
3761
|
+
tools: z$1.array(CompletionToolSchema).optional(),
|
|
3762
|
+
response_format: ResponseFormatSchema.optional(),
|
|
3763
|
+
reasoningEffort: CompletionReasoningEffortSchema.optional().describe("Reasoning effort hint. Honoured by the OpenAI (reasoning models only, and not alongside tools on some of them), Kimi (K3 only), DeepSeek and Anthropic Claude (direct and Bedrock) adapters; each maps it onto its own scale. On Claude, `none` turns thinking off where the model allows it (otherwise it runs at the lowest effort), so short `max_tokens` replies stream visible text from the start; `minimal` maps to `low`. When `thinking.enabled` is also set on a Claude model, thinking stays on and this sets its effort instead, with `none` and `minimal` mapping to `low`. Claude models that reason inside `max_tokens` never have that ceiling raised, and on older Claude models any level above `none` turns thinking on (it is off by default there), forces temperature to 1 and drops top_p, and a thinking budget that cannot fit under `max_tokens` is skipped. On the direct Anthropic API, a json_schema `response_format` with no tools is served by a forced tool on models that support forced tool use, and that tool cannot run alongside thinking, so there only `none` is honoured; models served by native structured outputs honour every level. Silently ignored by every other adapter (Gemini, xAI, Ollama) and by non-reasoning models, so sending it there is a no-op."),
|
|
3764
|
+
thinking: z$1.object({
|
|
3765
|
+
enabled: z$1.boolean(),
|
|
3766
|
+
budget_tokens: z$1.number().int().min(1024).optional()
|
|
3767
|
+
}).optional().describe("Ask a model that can think to think on this turn. On Claude, `reasoningEffort` then sets how much it thinks, and `budget_tokens` (default 16000, minimum 1024) sizes thinking on older Claude models. Ignored by models that cannot think, and on the direct Anthropic API when a json_schema `response_format` is served by a forced tool, which cannot run alongside thinking.")
|
|
3768
|
+
}).optional()
|
|
3769
|
+
});
|
|
3770
|
+
const CompletionUsageSchema = z$1.object({
|
|
3771
|
+
inputTokens: z$1.number().optional(),
|
|
3772
|
+
outputTokens: z$1.number().optional(),
|
|
3773
|
+
reasoningTokens: z$1.number().optional().describe("Reasoning/thinking tokens the provider reported. A subset of outputTokens, not additive and not billed on top. Absent when the provider or adapter reports no count."),
|
|
3774
|
+
cacheReadInputTokens: z$1.number().optional(),
|
|
3775
|
+
cacheCreationInputTokens: z$1.number().optional()
|
|
3776
|
+
});
|
|
3777
|
+
const CompletionMetaEventSchema = z$1.object({
|
|
3778
|
+
type: z$1.literal("meta"),
|
|
3779
|
+
requestId: z$1.string()
|
|
3780
|
+
});
|
|
3781
|
+
const CompletionContentEventSchema = z$1.object({
|
|
3782
|
+
type: z$1.enum(["content", "tool_use"]),
|
|
3783
|
+
text: z$1.string(),
|
|
3784
|
+
tools: z$1.array(z$1.object({
|
|
3785
|
+
name: z$1.string(),
|
|
3786
|
+
arguments: z$1.string().optional(),
|
|
3787
|
+
id: z$1.string().optional()
|
|
3788
|
+
})).optional(),
|
|
3789
|
+
usage: CompletionUsageSchema.optional(),
|
|
3790
|
+
credits: z$1.object({
|
|
3791
|
+
used: z$1.number().optional(),
|
|
3792
|
+
usdCost: z$1.number().optional()
|
|
3793
|
+
}).optional(),
|
|
3794
|
+
responseFormatMode: z$1.enum([
|
|
3795
|
+
"native",
|
|
3796
|
+
"tool_use",
|
|
3797
|
+
"best-effort"
|
|
3798
|
+
]).optional(),
|
|
3799
|
+
stopReason: z$1.string().optional(),
|
|
3800
|
+
thinking: z$1.array(z$1.any()).optional(),
|
|
3801
|
+
toolStarted: z$1.object({
|
|
3802
|
+
name: z$1.string(),
|
|
3803
|
+
id: z$1.string().optional()
|
|
3804
|
+
}).optional()
|
|
3805
|
+
});
|
|
3806
|
+
/**
|
|
3807
|
+
* Classifiers the completions `error` frame may carry: the persisted quest codes plus the two
|
|
3808
|
+
* provider codes. A separate tuple because QUEST_ERROR_CODES is stored on quests and must not widen.
|
|
3809
|
+
*/
|
|
3810
|
+
const COMPLETION_SSE_ERROR_CODES = [
|
|
3811
|
+
...QUEST_ERROR_CODES,
|
|
3812
|
+
"provider_error",
|
|
3813
|
+
"provider_rejected",
|
|
3814
|
+
"content_blocked"
|
|
3815
|
+
];
|
|
3816
|
+
/**
|
|
3817
|
+
* The in-band terminal failure frame, sent only once the stream has opened. Failures found before
|
|
3818
|
+
* the provider call (auth, scope, validation, rate limit, model, credits) are JSON errors with a
|
|
3819
|
+
* real status instead; see completions.contract.ts.
|
|
3820
|
+
*
|
|
3821
|
+
* `code` is the machine-readable classifier: a billing code when settlement runs out mid-generation,
|
|
3822
|
+
* `provider_rejected` / `provider_error` when the model provider fails, `content_blocked` when the
|
|
3823
|
+
* model's safety classifier declined the request. Optional; callers must never match on the prose
|
|
3824
|
+
* `message`.
|
|
3825
|
+
*
|
|
3826
|
+
* The field is `code` rather than the conventions' `errorCode` because this frame shipped that way
|
|
3827
|
+
* and is a published wire shape.
|
|
3828
|
+
*/
|
|
3829
|
+
const CompletionSseErrorEventSchema = z$1.object({
|
|
3830
|
+
type: z$1.literal("error"),
|
|
3831
|
+
message: z$1.string(),
|
|
3832
|
+
requestId: z$1.string().optional(),
|
|
3833
|
+
code: z$1.enum(COMPLETION_SSE_ERROR_CODES).optional()
|
|
3834
|
+
});
|
|
3835
|
+
/**
|
|
3836
|
+
* A provider content block the caller must echo back verbatim, in stream order, in the assistant
|
|
3837
|
+
* `messages` content of the next request: today the Anthropic tool search `server_tool_use` and
|
|
3838
|
+
* `tool_search_tool_result` blocks. Never executed by the caller and never answered with a tool_result.
|
|
3839
|
+
*/
|
|
3840
|
+
const CompletionProviderBlockEventSchema = z$1.object({
|
|
3841
|
+
type: z$1.literal("provider_block"),
|
|
3842
|
+
provider: z$1.literal("anthropic"),
|
|
3843
|
+
block: z$1.record(z$1.string(), z$1.unknown())
|
|
3844
|
+
});
|
|
3845
|
+
/** One `data:` event in the `text/event-stream` completions response. */
|
|
3846
|
+
const CompletionStreamEventSchema = z$1.union([
|
|
3847
|
+
CompletionMetaEventSchema,
|
|
3848
|
+
CompletionContentEventSchema,
|
|
3849
|
+
CompletionProviderBlockEventSchema,
|
|
3850
|
+
CompletionSseErrorEventSchema
|
|
3851
|
+
]);
|
|
3646
3852
|
/** Splits on the marker tokens, keeping them in the result so a scan can track nesting depth. */
|
|
3647
3853
|
const THINK_TAG_TOKENS = /(<think>|<\/think>)/g;
|
|
3648
3854
|
/**
|
|
@@ -3780,8 +3986,8 @@ const invalid = (reason) => ({
|
|
|
3780
3986
|
status: "invalid",
|
|
3781
3987
|
reason
|
|
3782
3988
|
});
|
|
3783
|
-
const FENCE_OPEN_LINE = /^[ \t]*(`{3,}|~{3,})(.*)$/;
|
|
3784
|
-
const FENCE_CLOSE_LINE = /^[ \t]*(`{3,}|~{3,})[ \t]*$/;
|
|
3989
|
+
const FENCE_OPEN_LINE$1 = /^[ \t]*(`{3,}|~{3,})(.*)$/;
|
|
3990
|
+
const FENCE_CLOSE_LINE$1 = /^[ \t]*(`{3,}|~{3,})[ \t]*$/;
|
|
3785
3991
|
/**
|
|
3786
3992
|
* Offset where a slot's visible answer starts: just past its last top-level `</think>`, 0 when it
|
|
3787
3993
|
* has none, or null while a `<think>` is still open. Depth-tracked to match visibleReplyText in
|
|
@@ -3811,7 +4017,7 @@ function findLastChoicesFence(text) {
|
|
|
3811
4017
|
const lineEnd = newline === -1 ? text.length : newline;
|
|
3812
4018
|
const line = text.slice(lineStart, lineEnd).replace(/\r$/, "");
|
|
3813
4019
|
if (!open) {
|
|
3814
|
-
const match = line.match(FENCE_OPEN_LINE);
|
|
4020
|
+
const match = line.match(FENCE_OPEN_LINE$1);
|
|
3815
4021
|
if (match && !(match[1][0] === "`" && match[2].includes("`"))) open = {
|
|
3816
4022
|
start: lineStart,
|
|
3817
4023
|
bodyStart: newline === -1 ? text.length : newline + 1,
|
|
@@ -3819,7 +4025,7 @@ function findLastChoicesFence(text) {
|
|
|
3819
4025
|
isChoices: match[1] === "```" && match[2].trim().toLowerCase() === "choices"
|
|
3820
4026
|
};
|
|
3821
4027
|
} else {
|
|
3822
|
-
const match = line.match(FENCE_CLOSE_LINE);
|
|
4028
|
+
const match = line.match(FENCE_CLOSE_LINE$1);
|
|
3823
4029
|
if (match && match[1][0] === open.marker[0] && match[1].length >= open.marker.length) {
|
|
3824
4030
|
if (open.isChoices) last = {
|
|
3825
4031
|
start: open.start,
|
|
@@ -4826,6 +5032,13 @@ z$1.object({
|
|
|
4826
5032
|
reason: z$1.enum(REPLY_CHOICES_INVALID_REASONS).optional()
|
|
4827
5033
|
}).optional(),
|
|
4828
5034
|
/**
|
|
5035
|
+
* Set when finalize stripped a tool call the model wrote into its reply as text instead of
|
|
5036
|
+
* calling it (see ../utils/narratedToolCalls.ts). The call never ran; `toolNames` are the
|
|
5037
|
+
* offered tools it named. Absent when nothing was stripped. Consumers read this rather than
|
|
5038
|
+
* matching the reply text, which no longer carries the call.
|
|
5039
|
+
*/
|
|
5040
|
+
narratedToolCall: z$1.object({ toolNames: z$1.array(z$1.string()) }).optional(),
|
|
5041
|
+
/**
|
|
4829
5042
|
* Set when an emitted artifact looks voluntarily abbreviated - placeholder comments in
|
|
4830
5043
|
* place of real code, or calls into functions that were never defined. The complement to
|
|
4831
5044
|
* `finishReason === 'max_tokens'`: truncation is a hard stop the provider reports, elision
|
|
@@ -5064,8 +5277,11 @@ const ChatWaitTimeoutErrorSchema = ApiErrorSchema.extend({
|
|
|
5064
5277
|
const StorageLimitErrorSchema = ApiErrorSchema.extend(StorageLimitErrorInfoSchema.partial().shape);
|
|
5065
5278
|
/** 503 when this deployment has no usable key for a provider the request needs (data-lake search, voice). */
|
|
5066
5279
|
const ProviderNotConfiguredErrorSchema = ApiErrorSchema.extend({ errorCode: z$1.literal("provider_not_configured").optional() });
|
|
5067
|
-
/**
|
|
5068
|
-
|
|
5280
|
+
/**
|
|
5281
|
+
* 400 for a `model` the caller cannot use, or a refused tool search request; `errorCode` is absent on the
|
|
5282
|
+
* other 400s (e.g. malformed JSON).
|
|
5283
|
+
*/
|
|
5284
|
+
const CompletionsBadRequestErrorSchema = ApiErrorSchema.extend({ errorCode: z$1.enum([UNKNOWN_MODEL_ERROR_CODE, ...COMPLETION_TOOL_ERROR_CODES]).optional() });
|
|
5069
5285
|
/**
|
|
5070
5286
|
* Error envelope for the 403 apiKeyAuth (apps/client/server/middlewares/apiKeyAuth.ts)
|
|
5071
5287
|
* throws when an API key lacks a route's scopes. It names what the ROUTE requires,
|
|
@@ -5989,34 +6205,95 @@ function parseCidr(raw) {
|
|
|
5989
6205
|
prefix: Number(value.slice(slash + 1))
|
|
5990
6206
|
};
|
|
5991
6207
|
}
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
6208
|
+
/**
|
|
6209
|
+
* Networks a private-network opt-in never reaches, whatever the operator lists, self-host included.
|
|
6210
|
+
* One list for both sides: the settings validator refuses any range that overlaps an entry, and the
|
|
6211
|
+
* fetch path (`buildNeverAdmitted` in fab-pipeline's pinnedFetch.ts) refuses these at connect time.
|
|
6212
|
+
* IPv4 entries also cover their IPv4-mapped IPv6 forms (`::ffff:0:0/96`) on both sides.
|
|
6213
|
+
*/
|
|
6214
|
+
const NEVER_ADMITTED_NETWORKS = [
|
|
6215
|
+
{
|
|
6216
|
+
cidr: "0.0.0.0/8",
|
|
6217
|
+
label: "this network"
|
|
6218
|
+
},
|
|
6219
|
+
{
|
|
6220
|
+
cidr: "127.0.0.0/8",
|
|
6221
|
+
label: "loopback"
|
|
6222
|
+
},
|
|
6223
|
+
{
|
|
6224
|
+
cidr: "169.254.0.0/16",
|
|
6225
|
+
label: "link-local, including cloud metadata"
|
|
6226
|
+
},
|
|
6227
|
+
{
|
|
6228
|
+
cidr: "100.100.100.200/32",
|
|
6229
|
+
label: "cloud metadata"
|
|
6230
|
+
},
|
|
6231
|
+
{
|
|
6232
|
+
cidr: "fd00:ec2::254/128",
|
|
6233
|
+
label: "cloud metadata"
|
|
6234
|
+
},
|
|
6235
|
+
{
|
|
6236
|
+
cidr: "64:ff9b::a9fe:a9fe/128",
|
|
6237
|
+
label: "cloud metadata"
|
|
6238
|
+
},
|
|
6239
|
+
{
|
|
6240
|
+
cidr: "::/128",
|
|
6241
|
+
label: "unspecified"
|
|
6242
|
+
},
|
|
6243
|
+
{
|
|
6244
|
+
cidr: "::1/128",
|
|
6245
|
+
label: "loopback"
|
|
6246
|
+
},
|
|
6247
|
+
{
|
|
6248
|
+
cidr: "fe80::/10",
|
|
6249
|
+
label: "link-local"
|
|
6250
|
+
}
|
|
6251
|
+
];
|
|
6252
|
+
const IPV4_MAPPED_PREFIX = [
|
|
6253
|
+
0,
|
|
6254
|
+
0,
|
|
6255
|
+
0,
|
|
6256
|
+
0,
|
|
6257
|
+
0,
|
|
6258
|
+
0,
|
|
6259
|
+
0,
|
|
6260
|
+
0,
|
|
6261
|
+
0,
|
|
6262
|
+
0,
|
|
6263
|
+
255,
|
|
6264
|
+
255
|
|
6265
|
+
];
|
|
6266
|
+
/** An IPv4 CIDR in its IPv4-mapped IPv6 form; other CIDRs unchanged. */
|
|
6267
|
+
function asIpv6(cidr) {
|
|
6268
|
+
if (cidr.version === 6) return cidr;
|
|
6269
|
+
return {
|
|
6270
|
+
version: 6,
|
|
6271
|
+
bytes: [...IPV4_MAPPED_PREFIX, ...cidr.bytes],
|
|
6272
|
+
prefix: cidr.prefix + 96
|
|
6273
|
+
};
|
|
6274
|
+
}
|
|
6275
|
+
function cidrsOverlap(a, b) {
|
|
6276
|
+
const [left, right] = a.version === b.version ? [a, b] : [asIpv6(a), asIpv6(b)];
|
|
6277
|
+
const prefix = Math.min(left.prefix, right.prefix);
|
|
6278
|
+
for (let bit = 0; bit < prefix; bit++) {
|
|
5996
6279
|
const byte = bit >> 3;
|
|
5997
6280
|
const mask = 128 >> (bit & 7);
|
|
5998
|
-
if (((
|
|
6281
|
+
if (((left.bytes[byte] ?? 0) & mask) !== ((right.bytes[byte] ?? 0) & mask)) return false;
|
|
5999
6282
|
}
|
|
6000
6283
|
return true;
|
|
6001
6284
|
}
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
const CLOUD_METADATA_ADDRESSES = [
|
|
6007
|
-
"169.254.169.254",
|
|
6008
|
-
"fd00:ec2::254",
|
|
6009
|
-
"::ffff:169.254.169.254",
|
|
6010
|
-
"64:ff9b::169.254.169.254"
|
|
6011
|
-
];
|
|
6285
|
+
const NEVER_ADMITTED_PARSED = NEVER_ADMITTED_NETWORKS.map((entry) => ({
|
|
6286
|
+
...entry,
|
|
6287
|
+
parsed: parseCidr(entry.cidr)
|
|
6288
|
+
}));
|
|
6012
6289
|
/** Why `raw` may not be allowed as a private network, or null when it may. */
|
|
6013
6290
|
function customMcpPrivateCidrProblem(raw) {
|
|
6014
6291
|
const cidr = parseCidr(raw);
|
|
6015
6292
|
if (!cidr) return "Must be a CIDR such as 10.0.0.0/8 or fd12:3456::/32";
|
|
6016
6293
|
const minPrefix = CUSTOM_MCP_MIN_CIDR_PREFIX[cidr.version];
|
|
6017
6294
|
if (cidr.prefix < minPrefix) return `IPv${cidr.version} ranges must be /${minPrefix} or narrower`;
|
|
6018
|
-
const
|
|
6019
|
-
if (
|
|
6295
|
+
const blocked = NEVER_ADMITTED_PARSED.find((entry) => cidrsOverlap(cidr, entry.parsed));
|
|
6296
|
+
if (blocked) return `Range overlaps ${blocked.cidr} (${blocked.label}), which is never reachable`;
|
|
6020
6297
|
return null;
|
|
6021
6298
|
}
|
|
6022
6299
|
const CustomMcpPrivateCidrSchema = z$1.string().trim().toLowerCase().superRefine((value, ctx) => {
|
|
@@ -7020,8 +7297,8 @@ const CustomMcpPolicySchema = z$1.object({
|
|
|
7020
7297
|
const CustomMcpBlockedHostsSchema = z$1.object({ hosts: McpHostPatternListSchema.default([]) });
|
|
7021
7298
|
/**
|
|
7022
7299
|
* Self-host only; hosted deployments never reach private networks regardless of this value. Each
|
|
7023
|
-
* CIDR is strictly parsed, at least /8 (IPv4) or /16 (IPv6), and never
|
|
7024
|
-
*
|
|
7300
|
+
* CIDR is strictly parsed, at least /8 (IPv4) or /16 (IPv6), and never overlaps a network the fetch
|
|
7301
|
+
* path refuses anyway (NEVER_ADMITTED_NETWORKS in customMcpHosts.ts).
|
|
7025
7302
|
*/
|
|
7026
7303
|
const CustomMcpAllowPrivateNetworksSchema = z$1.object({
|
|
7027
7304
|
cidrs: z$1.array(CustomMcpPrivateCidrSchema).max(100).default([]),
|
|
@@ -11733,16 +12010,8 @@ const CliCompletionRequestAction = z$1.object({
|
|
|
11733
12010
|
temperature: z$1.number().optional(),
|
|
11734
12011
|
maxTokens: z$1.number().optional(),
|
|
11735
12012
|
stream: z$1.boolean().optional(),
|
|
11736
|
-
tools: z$1.array(
|
|
11737
|
-
response_format:
|
|
11738
|
-
type: z$1.literal("json_schema"),
|
|
11739
|
-
json_schema: z$1.object({
|
|
11740
|
-
name: z$1.string(),
|
|
11741
|
-
description: z$1.string().optional(),
|
|
11742
|
-
schema: z$1.record(z$1.string(), z$1.any()),
|
|
11743
|
-
strict: z$1.boolean().optional().default(true)
|
|
11744
|
-
})
|
|
11745
|
-
})]).optional()
|
|
12013
|
+
tools: z$1.array(CompletionToolSchema).optional(),
|
|
12014
|
+
response_format: ResponseFormatSchema.optional()
|
|
11746
12015
|
}).optional()
|
|
11747
12016
|
});
|
|
11748
12017
|
/**
|
|
@@ -12231,7 +12500,7 @@ const VoiceCreditsExhaustedAction = z$1.object({
|
|
|
12231
12500
|
const CliCompletionChunkAction = z$1.object({
|
|
12232
12501
|
action: z$1.literal("cli_completion_chunk"),
|
|
12233
12502
|
requestId: z$1.string(),
|
|
12234
|
-
chunk: z$1.object({
|
|
12503
|
+
chunk: z$1.union([z$1.object({
|
|
12235
12504
|
type: z$1.enum(["content", "tool_use"]),
|
|
12236
12505
|
text: z$1.string(),
|
|
12237
12506
|
toolStarted: z$1.object({
|
|
@@ -12244,7 +12513,7 @@ const CliCompletionChunkAction = z$1.object({
|
|
|
12244
12513
|
outputTokens: z$1.number().optional()
|
|
12245
12514
|
}).optional(),
|
|
12246
12515
|
thinking: z$1.array(z$1.unknown()).optional()
|
|
12247
|
-
})
|
|
12516
|
+
}), CompletionProviderBlockEventSchema])
|
|
12248
12517
|
});
|
|
12249
12518
|
/**
|
|
12250
12519
|
* CLI Completion stream finished successfully
|
|
@@ -12261,7 +12530,9 @@ const CliCompletionErrorAction = z$1.object({
|
|
|
12261
12530
|
requestId: z$1.string(),
|
|
12262
12531
|
error: z$1.string(),
|
|
12263
12532
|
/** Seconds until the caller's rate-limit window reopens; present only on a rate-limit refusal. */
|
|
12264
|
-
retryAfter: z$1.number().optional()
|
|
12533
|
+
retryAfter: z$1.number().optional(),
|
|
12534
|
+
/** Machine-readable reason on a request the caller must fix (a 4xx), e.g. `tool_search_unsupported`. */
|
|
12535
|
+
errorCode: z$1.string().optional()
|
|
12265
12536
|
});
|
|
12266
12537
|
/**
|
|
12267
12538
|
* CLI Tool execution response over WebSocket
|
|
@@ -12912,7 +13183,12 @@ const AgentProgressAction = z$1.object({
|
|
|
12912
13183
|
generationJobs: z$1.array(z$1.object({
|
|
12913
13184
|
id: z$1.string(),
|
|
12914
13185
|
kind: z$1.enum(GENERATION_JOB_KINDS)
|
|
12915
|
-
})).optional()
|
|
13186
|
+
})).optional(),
|
|
13187
|
+
pendingAction: z$1.object({
|
|
13188
|
+
tool: z$1.string(),
|
|
13189
|
+
params: z$1.record(z$1.string(), z$1.unknown()),
|
|
13190
|
+
ts: z$1.number()
|
|
13191
|
+
}).optional()
|
|
12916
13192
|
});
|
|
12917
13193
|
const AgentCompletedAction = z$1.object({
|
|
12918
13194
|
action: z$1.literal("completed"),
|
|
@@ -13376,165 +13652,6 @@ const AgentExecutionStatusResponseSchema = z$1.object({
|
|
|
13376
13652
|
});
|
|
13377
13653
|
/** Path parameter for `GET /api/v1/agent-executions/{id}`. */
|
|
13378
13654
|
const AgentExecutionIdParamSchema = z$1.object({ id: z$1.string().min(1) });
|
|
13379
|
-
const CompletionReasoningEffortSchema = z$1.enum([
|
|
13380
|
-
"none",
|
|
13381
|
-
"minimal",
|
|
13382
|
-
"low",
|
|
13383
|
-
"medium",
|
|
13384
|
-
"high",
|
|
13385
|
-
"xhigh"
|
|
13386
|
-
]);
|
|
13387
|
-
/**
|
|
13388
|
-
* Tool schema matching ICompletionOptionTools.toolSchema. The Zod surface only
|
|
13389
|
-
* covers wire-format fields (toolFn is server-side). Replaces the historical
|
|
13390
|
-
* z.array(z.any()) so client-side bugs (typos in name, missing type) surface
|
|
13391
|
-
* at parse time instead of failing silently inside the backend.
|
|
13392
|
-
*/
|
|
13393
|
-
const CompletionToolSchema = z$1.object({
|
|
13394
|
-
toolSchema: z$1.object({
|
|
13395
|
-
name: z$1.string(),
|
|
13396
|
-
description: z$1.string(),
|
|
13397
|
-
parameters: z$1.object({
|
|
13398
|
-
type: z$1.literal("object"),
|
|
13399
|
-
properties: z$1.record(z$1.string(), z$1.any()).optional(),
|
|
13400
|
-
additionalProperties: z$1.boolean().optional(),
|
|
13401
|
-
required: z$1.array(z$1.string()).optional()
|
|
13402
|
-
}).passthrough(),
|
|
13403
|
-
strict: z$1.boolean().optional()
|
|
13404
|
-
}),
|
|
13405
|
-
_isMcpTool: z$1.boolean().optional()
|
|
13406
|
-
});
|
|
13407
|
-
/**
|
|
13408
|
-
* JSON Schema definition for structured output. The `schema` field is a full
|
|
13409
|
-
* JSON Schema document (we don't try to validate JSON Schema with Zod - that's
|
|
13410
|
-
* the caller's contract with the model). `strict: true` is the default, matching
|
|
13411
|
-
* OpenAI's strict-mode subset semantics.
|
|
13412
|
-
*/
|
|
13413
|
-
const ResponseFormatSchema = z$1.discriminatedUnion("type", [z$1.object({ type: z$1.literal("text") }), z$1.object({
|
|
13414
|
-
type: z$1.literal("json_schema"),
|
|
13415
|
-
json_schema: z$1.object({
|
|
13416
|
-
name: z$1.string(),
|
|
13417
|
-
description: z$1.string().optional(),
|
|
13418
|
-
schema: z$1.record(z$1.string(), z$1.any()),
|
|
13419
|
-
strict: z$1.boolean().optional().default(true)
|
|
13420
|
-
})
|
|
13421
|
-
})]);
|
|
13422
|
-
/**
|
|
13423
|
-
* Wire-format message for /api/ai/v1/completions. The optional `cache: true`
|
|
13424
|
-
* flag is honored by Anthropic (translated to `cache_control: { type: 'ephemeral' }`
|
|
13425
|
-
* with the appropriate beta header) and silently ignored by other providers.
|
|
13426
|
-
*/
|
|
13427
|
-
const CompletionMessageSchema = z$1.object({
|
|
13428
|
-
role: z$1.enum([
|
|
13429
|
-
"user",
|
|
13430
|
-
"assistant",
|
|
13431
|
-
"system"
|
|
13432
|
-
]),
|
|
13433
|
-
content: z$1.union([z$1.string(), z$1.array(z$1.any())]),
|
|
13434
|
-
cache: z$1.boolean().optional()
|
|
13435
|
-
});
|
|
13436
|
-
/**
|
|
13437
|
-
* Schema for CLI LLM completion requests
|
|
13438
|
-
* Shared between Next.js API route (dev) and Lambda function (production)
|
|
13439
|
-
*
|
|
13440
|
-
* `response_format`, `stream`, `tools`, `temperature`, and `max_tokens` are all
|
|
13441
|
-
* accepted at the top level (OpenAI-compatible, matching how every major LLM
|
|
13442
|
-
* SDK shapes a completion request) AND nested under `options` (legacy shape).
|
|
13443
|
-
* Use `normalizeCompletionRequest()` to collapse both surfaces into the
|
|
13444
|
-
* canonical `options.<field>` location before downstream consumption.
|
|
13445
|
-
*/
|
|
13446
|
-
const CompletionRequestSchema = z$1.object({
|
|
13447
|
-
model: z$1.string().max(256),
|
|
13448
|
-
messages: z$1.array(CompletionMessageSchema),
|
|
13449
|
-
response_format: ResponseFormatSchema.optional(),
|
|
13450
|
-
stream: z$1.boolean().optional(),
|
|
13451
|
-
tools: z$1.array(CompletionToolSchema).optional(),
|
|
13452
|
-
temperature: z$1.number().optional(),
|
|
13453
|
-
max_tokens: z$1.number().optional(),
|
|
13454
|
-
options: z$1.object({
|
|
13455
|
-
temperature: z$1.number().optional(),
|
|
13456
|
-
maxTokens: z$1.number().optional(),
|
|
13457
|
-
stream: z$1.boolean().optional(),
|
|
13458
|
-
tools: z$1.array(CompletionToolSchema).optional(),
|
|
13459
|
-
response_format: ResponseFormatSchema.optional(),
|
|
13460
|
-
reasoningEffort: CompletionReasoningEffortSchema.optional().describe("Reasoning effort hint. Honoured by the OpenAI (reasoning models only, and not alongside tools on some of them), Kimi (K3 only), DeepSeek and Anthropic Claude (direct and Bedrock) adapters; each maps it onto its own scale. On Claude, `none` turns thinking off where the model allows it (otherwise it runs at the lowest effort), so short `max_tokens` replies stream visible text from the start; `minimal` maps to `low`. When `thinking.enabled` is also set on a Claude model, thinking stays on and this sets its effort instead, with `none` and `minimal` mapping to `low`. Claude models that reason inside `max_tokens` never have that ceiling raised, and on older Claude models any level above `none` turns thinking on (it is off by default there), forces temperature to 1 and drops top_p, and a thinking budget that cannot fit under `max_tokens` is skipped. On the direct Anthropic API, a json_schema `response_format` with no tools is served by a forced tool on models that support forced tool use, and that tool cannot run alongside thinking, so there only `none` is honoured; models served by native structured outputs honour every level. Silently ignored by every other adapter (Gemini, xAI, Ollama) and by non-reasoning models, so sending it there is a no-op."),
|
|
13461
|
-
thinking: z$1.object({
|
|
13462
|
-
enabled: z$1.boolean(),
|
|
13463
|
-
budget_tokens: z$1.number().int().min(1024).optional()
|
|
13464
|
-
}).optional().describe("Ask a model that can think to think on this turn. On Claude, `reasoningEffort` then sets how much it thinks, and `budget_tokens` (default 16000, minimum 1024) sizes thinking on older Claude models. Ignored by models that cannot think, and on the direct Anthropic API when a json_schema `response_format` is served by a forced tool, which cannot run alongside thinking.")
|
|
13465
|
-
}).optional()
|
|
13466
|
-
});
|
|
13467
|
-
const CompletionUsageSchema = z$1.object({
|
|
13468
|
-
inputTokens: z$1.number().optional(),
|
|
13469
|
-
outputTokens: z$1.number().optional(),
|
|
13470
|
-
reasoningTokens: z$1.number().optional().describe("Reasoning/thinking tokens the provider reported. A subset of outputTokens, not additive and not billed on top. Absent when the provider or adapter reports no count."),
|
|
13471
|
-
cacheReadInputTokens: z$1.number().optional(),
|
|
13472
|
-
cacheCreationInputTokens: z$1.number().optional()
|
|
13473
|
-
});
|
|
13474
|
-
const CompletionMetaEventSchema = z$1.object({
|
|
13475
|
-
type: z$1.literal("meta"),
|
|
13476
|
-
requestId: z$1.string()
|
|
13477
|
-
});
|
|
13478
|
-
const CompletionContentEventSchema = z$1.object({
|
|
13479
|
-
type: z$1.enum(["content", "tool_use"]),
|
|
13480
|
-
text: z$1.string(),
|
|
13481
|
-
tools: z$1.array(z$1.object({
|
|
13482
|
-
name: z$1.string(),
|
|
13483
|
-
arguments: z$1.string().optional(),
|
|
13484
|
-
id: z$1.string().optional()
|
|
13485
|
-
})).optional(),
|
|
13486
|
-
usage: CompletionUsageSchema.optional(),
|
|
13487
|
-
credits: z$1.object({
|
|
13488
|
-
used: z$1.number().optional(),
|
|
13489
|
-
usdCost: z$1.number().optional()
|
|
13490
|
-
}).optional(),
|
|
13491
|
-
responseFormatMode: z$1.enum([
|
|
13492
|
-
"native",
|
|
13493
|
-
"tool_use",
|
|
13494
|
-
"best-effort"
|
|
13495
|
-
]).optional(),
|
|
13496
|
-
stopReason: z$1.string().optional(),
|
|
13497
|
-
thinking: z$1.array(z$1.any()).optional(),
|
|
13498
|
-
toolStarted: z$1.object({
|
|
13499
|
-
name: z$1.string(),
|
|
13500
|
-
id: z$1.string().optional()
|
|
13501
|
-
}).optional()
|
|
13502
|
-
});
|
|
13503
|
-
/**
|
|
13504
|
-
* Classifiers the completions `error` frame may carry: the persisted quest codes plus the two
|
|
13505
|
-
* provider codes. A separate tuple because QUEST_ERROR_CODES is stored on quests and must not widen.
|
|
13506
|
-
*/
|
|
13507
|
-
const COMPLETION_SSE_ERROR_CODES = [
|
|
13508
|
-
...QUEST_ERROR_CODES,
|
|
13509
|
-
"provider_error",
|
|
13510
|
-
"provider_rejected",
|
|
13511
|
-
"content_blocked"
|
|
13512
|
-
];
|
|
13513
|
-
/**
|
|
13514
|
-
* The in-band terminal failure frame, sent only once the stream has opened. Failures found before
|
|
13515
|
-
* the provider call (auth, scope, validation, rate limit, model, credits) are JSON errors with a
|
|
13516
|
-
* real status instead; see completions.contract.ts.
|
|
13517
|
-
*
|
|
13518
|
-
* `code` is the machine-readable classifier: a billing code when settlement runs out mid-generation,
|
|
13519
|
-
* `provider_rejected` / `provider_error` when the model provider fails, `content_blocked` when the
|
|
13520
|
-
* model's safety classifier declined the request. Optional; callers must never match on the prose
|
|
13521
|
-
* `message`.
|
|
13522
|
-
*
|
|
13523
|
-
* The field is `code` rather than the conventions' `errorCode` because this frame shipped that way
|
|
13524
|
-
* and is a published wire shape.
|
|
13525
|
-
*/
|
|
13526
|
-
const CompletionSseErrorEventSchema = z$1.object({
|
|
13527
|
-
type: z$1.literal("error"),
|
|
13528
|
-
message: z$1.string(),
|
|
13529
|
-
requestId: z$1.string().optional(),
|
|
13530
|
-
code: z$1.enum(COMPLETION_SSE_ERROR_CODES).optional()
|
|
13531
|
-
});
|
|
13532
|
-
/** One `data:` event in the `text/event-stream` completions response. */
|
|
13533
|
-
const CompletionStreamEventSchema = z$1.union([
|
|
13534
|
-
CompletionMetaEventSchema,
|
|
13535
|
-
CompletionContentEventSchema,
|
|
13536
|
-
CompletionSseErrorEventSchema
|
|
13537
|
-
]);
|
|
13538
13655
|
/**
|
|
13539
13656
|
* Server-side tool execution schemas for POST /api/ai/v1/tools.
|
|
13540
13657
|
*
|
|
@@ -14943,6 +15060,10 @@ const questMasterPlanResponseSchema = z$2.object(passthroughShape({
|
|
|
14943
15060
|
decisions: true
|
|
14944
15061
|
}));
|
|
14945
15062
|
z$2.array(questMasterPlanResponseSchema);
|
|
15063
|
+
z$2.object({
|
|
15064
|
+
success: z$2.literal(true),
|
|
15065
|
+
plan: questMasterPlanResponseSchema
|
|
15066
|
+
}).extend({ metrics: passthroughField });
|
|
14946
15067
|
z$1.preprocess((val) => {
|
|
14947
15068
|
if (typeof val === "string") {
|
|
14948
15069
|
if (val.toLowerCase() === "true") return true;
|
|
@@ -16249,8 +16370,8 @@ const ListModelsResponseSchema = paginatedResponseSchema(z$1.object({
|
|
|
16249
16370
|
reasons_within_output_budget: z$1.boolean().describe("Whether reasoning tokens are spent inside `max_output_tokens` on every turn. When true, a small `max_tokens` can be used up by reasoning alone and return an empty reply, so leave room above the answer you expect."),
|
|
16250
16371
|
reasoning_efforts: z$1.array(CompletionReasoningEffortSchema).describe("The `reasoningEffort` levels this model acts on, lowest first; empty when it ignores the option. `none` is listed only where it turns reasoning off. Each level is mapped to the nearest one the provider offers. A listed level can still be dropped on one request: some OpenAI models drop it on tool turns, and Claude models with a fixed thinking budget drop it when `max_tokens` leaves no room for the budget plus the answer."),
|
|
16251
16372
|
supports_tools: z$1.boolean(),
|
|
16252
|
-
supports_tool_search: z$1.boolean().describe("Whether the model accepts the tool search server tool and `defer_loading: true` on tool definitions, so a large tool catalog is searched on demand instead of loaded into context up front. Describes the model
|
|
16253
|
-
supports_tool_changes: z$1.boolean().describe("Whether a system-role message can carry `tool_addition` and `tool_removal` blocks, which add or remove a declared tool mid-conversation. Describes the model only: `/api/ai/v1/completions` does not accept these blocks yet
|
|
16373
|
+
supports_tool_search: z$1.boolean().describe("Whether the model accepts the tool search server tool and `defer_loading: true` on tool definitions, so a large tool catalog is searched on demand instead of loaded into context up front. Describes the model: `/api/ai/v1/completions` honours it only for models whose `backend` is `anthropic`. A `bedrock` model with this flag set is not served with tool search there yet: a `serverTool` entry is refused with `errorCode: \"tool_search_unsupported\"` and `defer_loading` is ignored."),
|
|
16374
|
+
supports_tool_changes: z$1.boolean().describe("Whether a system-role message can carry `tool_addition` and `tool_removal` blocks, which add or remove a declared tool mid-conversation. Describes the model only: `/api/ai/v1/completions` does not accept these blocks yet, and will accept them first for models whose `backend` is `anthropic`."),
|
|
16254
16375
|
supports_vision: z$1.boolean(),
|
|
16255
16376
|
deprecation_date: z$1.string().nullable().describe("`YYYY-MM-DD`; null when no retirement is scheduled."),
|
|
16256
16377
|
replaced_by: z$1.string().nullable().describe("The id of the recommended successor, when there is one."),
|
|
@@ -16644,7 +16765,12 @@ z$1.object({
|
|
|
16644
16765
|
* Session-creation input, not a completion parameter, so it is applied only when `sessionId`
|
|
16645
16766
|
* is absent; ignored when an existing session is resolved.
|
|
16646
16767
|
*/
|
|
16647
|
-
agentIds: z$1.array(z$1.string()).optional()
|
|
16768
|
+
agentIds: z$1.array(z$1.string()).optional(),
|
|
16769
|
+
/**
|
|
16770
|
+
* The subset of `messageFileIds` the user scoped to this message only. Every other notebook-scope
|
|
16771
|
+
* message attachment is pinned to the session so it stays in context on later turns.
|
|
16772
|
+
*/
|
|
16773
|
+
messageOnlyFileIds: z$1.array(z$1.string()).optional()
|
|
16648
16774
|
});
|
|
16649
16775
|
/**
|
|
16650
16776
|
* Response shapes for the public image endpoints (`generateImage` / `editImage`,
|
|
@@ -17311,7 +17437,7 @@ defineEndpoint({
|
|
|
17311
17437
|
path: "/api/ai/v1/completions",
|
|
17312
17438
|
operationId: "createCompletion",
|
|
17313
17439
|
summary: "Create a chat completion",
|
|
17314
|
-
description: "OpenAI-compatible completion. A successful request is ALWAYS answered with an SSE stream (`text/event-stream`), regardless of the `stream` flag: a `meta` event, then `content`/`tool_use` events carrying `usage`/`credits`, terminated by `data: [DONE]`. The terminal event carries `stopReason`; treat `max_tokens` as a TRUNCATED reply rather than a complete one, and note that omitting `max_tokens` on the request lets the server size the output ceiling for the model (recommended for reasoning models, which spend thinking tokens inside that ceiling). A message `content` may be a string or an array of parts; image parts are accepted in OpenAI Chat (`image_url`), OpenAI Responses (`input_image`) or Anthropic (`image` with a `source`) form and are translated to whatever the target model speaks. Authenticate with an API key (`b4m_live_`) or a JWT.\n\nERRORS. Everything checked before the model provider is called fails with a JSON error body and a real HTTP status, before any stream opens: a malformed or otherwise invalid request or an unknown `model` `400` (`errorCode: \"unknown_model\"` for the latter), a bad credential `401`, a key without a required scope `403` (with `required_scopes`), a body over 25 MB `413`, a body that fails schema validation `422`, insufficient credits `422` with `errorCode: \"insufficient_credits\"` (personal or org balance, or an org per-member credit cap), a rate limit `429` with `Retry-After`, and a catalogued model whose provider is not configured on this deployment `503` with `errorCode: \"provider_not_configured\"` (retryable). Once the stream has opened the status stays `200` and a failure arrives as one terminal in-band `error` event. Branch on that event's `code`, never on `message`, which is prose and may change: `insufficient_credits` (buy credits) when credits run out mid-generation, `provider_rejected` when the model provider refused our credentials, `content_blocked` when the model declined the request (do not retry it unchanged), and `provider_error` for any other provider failure. Treat `code` as optional.",
|
|
17440
|
+
description: "OpenAI-compatible completion. A successful request is ALWAYS answered with an SSE stream (`text/event-stream`), regardless of the `stream` flag: a `meta` event, then `content`/`tool_use` events carrying `usage`/`credits`, terminated by `data: [DONE]`. The terminal event carries `stopReason`; treat `max_tokens` as a TRUNCATED reply rather than a complete one, and note that omitting `max_tokens` on the request lets the server size the output ceiling for the model (recommended for reasoning models, which spend thinking tokens inside that ceiling). A message `content` may be a string or an array of parts; image parts are accepted in OpenAI Chat (`image_url`), OpenAI Responses (`input_image`) or Anthropic (`image` with a `source`) form and are translated to whatever the target model speaks. Authenticate with an API key (`b4m_live_`) or a JWT.\n\nTOOL SEARCH. On a model whose `GET /api/v1/models` row has `supports_tool_search: true` and `backend: \"anthropic\"`, a `tools` entry may set `toolSchema.defer_loading: true`, and a `{ \"serverTool\": { \"type\": \"tool_search_tool_regex_20251119\", \"name\": \"tool_search_tool_regex\" } }` (or the `bm25` pair) entry adds Anthropic's tool search, so deferred definitions load only when the model searches for them. The stream then carries `provider_block` events (`{ type, provider: \"anthropic\", block }`) for the server-run search; echo each block back verbatim, in order, in the assistant `messages` content of the next request, and never answer one with a `tool_result`. When the server-run search loop hits its iteration limit the stream ends with `stopReason: \"pause_turn\"`; continue by re-sending the conversation with that assistant turn's provider blocks echoed. A client-side search may return `{ \"type\": \"tool_reference\", \"tool_name\": \"...\" }` blocks inside a `tool_result`'s content. On every other model, Bedrock-served Claude included, `defer_loading` is ignored (every tool is sent as usual) and a server tool, a `tool_reference` or an echoed tool search block is refused with a `400` rather than run without it.\n\nERRORS. Everything checked before the model provider is called fails with a JSON error body and a real HTTP status, before any stream opens: a malformed or otherwise invalid request or an unknown `model` `400` (`errorCode: \"unknown_model\"` for the latter), a bad credential `401`, a key without a required scope `403` (with `required_scopes`), a body over 25 MB `413`, a body that fails schema validation `422`, insufficient credits `422` with `errorCode: \"insufficient_credits\"` (personal or org balance, or an org per-member credit cap), a rate limit `429` with `Retry-After`, and a catalogued model whose provider is not configured on this deployment `503` with `errorCode: \"provider_not_configured\"` (retryable). Once the stream has opened the status stays `200` and a failure arrives as one terminal in-band `error` event. Branch on that event's `code`, never on `message`, which is prose and may change: `insufficient_credits` (buy credits) when credits run out mid-generation, `provider_rejected` when the model provider refused our credentials, `content_blocked` when the model declined the request (do not retry it unchanged), and `provider_error` for any other provider failure. Treat `code` as optional.",
|
|
17315
17441
|
tags: ["AI"],
|
|
17316
17442
|
auth: "apiKeyOrJwt",
|
|
17317
17443
|
scopes: ["ai:chat", "ai:generate"],
|
|
@@ -17345,8 +17471,8 @@ defineEndpoint({
|
|
|
17345
17471
|
}
|
|
17346
17472
|
},
|
|
17347
17473
|
400: {
|
|
17348
|
-
description: "Malformed or otherwise invalid request (no `errorCode`), or a `model` the caller cannot use (`errorCode: \"unknown_model\"`): not in the catalog or not available to the caller. List valid ids with `GET /api/v1/models`.",
|
|
17349
|
-
schema:
|
|
17474
|
+
description: "Malformed or otherwise invalid request (no `errorCode`), or a `model` the caller cannot use (`errorCode: \"unknown_model\"`): not in the catalog or not available to the caller. List valid ids with `GET /api/v1/models`. Tool search requests are refused with: `tool_search_unsupported` (a server tool, `tool_reference` or echoed tool search block on a model that cannot serve tool search here), `all_tools_deferred` (every tool deferred and no server tool), `deferred_tool_cache_control` (`cache_control` on a deferred tool), `unknown_tool_reference` (a `tool_reference` naming a tool not in `tools`, or carrying no string `tool_name`).",
|
|
17475
|
+
schema: CompletionsBadRequestErrorSchema
|
|
17350
17476
|
},
|
|
17351
17477
|
413: {
|
|
17352
17478
|
description: "Request body over 25 MB.",
|
|
@@ -21178,7 +21304,7 @@ function scanArtifactOpenTag(text, at, leading, memo) {
|
|
|
21178
21304
|
end: close + 1
|
|
21179
21305
|
};
|
|
21180
21306
|
}
|
|
21181
|
-
const OPENER = /<artifact(?=\s)/gi;
|
|
21307
|
+
const OPENER$1 = /<artifact(?=\s)/gi;
|
|
21182
21308
|
const LEADING_WS = /\s+/y;
|
|
21183
21309
|
const CLOSER = /<\/artifact>/gi;
|
|
21184
21310
|
const GT = />/g;
|
|
@@ -21193,9 +21319,9 @@ function scanArtifactTags(text, attrsMayCrossLines) {
|
|
|
21193
21319
|
const stop = attrsMayCrossLines ? GT : GT_OR_LINE_TERMINATOR;
|
|
21194
21320
|
const out = [];
|
|
21195
21321
|
let termAt = -1;
|
|
21196
|
-
OPENER.lastIndex = 0;
|
|
21322
|
+
OPENER$1.lastIndex = 0;
|
|
21197
21323
|
let opener;
|
|
21198
|
-
while ((opener = OPENER.exec(text)) !== null) {
|
|
21324
|
+
while ((opener = OPENER$1.exec(text)) !== null) {
|
|
21199
21325
|
LEADING_WS.lastIndex = opener.index + opener[0].length;
|
|
21200
21326
|
LEADING_WS.exec(text);
|
|
21201
21327
|
const attrsStart = LEADING_WS.lastIndex;
|
|
@@ -21215,7 +21341,7 @@ function scanArtifactTags(text, attrsMayCrossLines) {
|
|
|
21215
21341
|
attrs: text.slice(attrsStart, termAt),
|
|
21216
21342
|
body: text.slice(termAt + 1, closer.index)
|
|
21217
21343
|
});
|
|
21218
|
-
OPENER.lastIndex = end;
|
|
21344
|
+
OPENER$1.lastIndex = end;
|
|
21219
21345
|
}
|
|
21220
21346
|
return out;
|
|
21221
21347
|
}
|
|
@@ -23793,111 +23919,273 @@ function getEnvironmentName(configApiConfig) {
|
|
|
23793
23919
|
return "Self-Hosted";
|
|
23794
23920
|
}
|
|
23795
23921
|
//#endregion
|
|
23796
|
-
//#region src/
|
|
23922
|
+
//#region src/tools/gateway/coreToolDescriptors.ts
|
|
23923
|
+
const CORE = { kind: "core" };
|
|
23924
|
+
const CLI = { kind: "cli" };
|
|
23925
|
+
const pureCompute = () => ({
|
|
23926
|
+
source: CORE,
|
|
23927
|
+
effect: "none",
|
|
23928
|
+
approval: "auto_approve"
|
|
23929
|
+
});
|
|
23930
|
+
const sessionTool = () => ({
|
|
23931
|
+
source: CLI,
|
|
23932
|
+
effect: "session",
|
|
23933
|
+
approval: "auto_approve"
|
|
23934
|
+
});
|
|
23935
|
+
const reader = (source = CORE, extra = {}) => ({
|
|
23936
|
+
source,
|
|
23937
|
+
effect: "read",
|
|
23938
|
+
approval: "prompt_default",
|
|
23939
|
+
...extra
|
|
23940
|
+
});
|
|
23941
|
+
const fileWriter = () => ({
|
|
23942
|
+
source: CORE,
|
|
23943
|
+
effect: "write",
|
|
23944
|
+
approval: "prompt_always",
|
|
23945
|
+
writePathArg: "path"
|
|
23946
|
+
});
|
|
23947
|
+
const lattice = () => ({
|
|
23948
|
+
source: CORE,
|
|
23949
|
+
effect: "none",
|
|
23950
|
+
approval: "prompt_default"
|
|
23951
|
+
});
|
|
23952
|
+
/**
|
|
23953
|
+
* Descriptors for every tool the CLI builds itself or gets from @bike4mind/services:
|
|
23954
|
+
* generateCliTools, buildCliToolList and the subagent-injected tools. A tool missing here
|
|
23955
|
+
* resolves to the unknown fallback in toolCatalog.ts; coreToolCompleteness.test.ts
|
|
23956
|
+
* fails when a generated tool has no entry.
|
|
23957
|
+
*/
|
|
23958
|
+
const CORE_TOOL_DESCRIPTORS = new Map(Object.entries({
|
|
23959
|
+
math_evaluate: pureCompute(),
|
|
23960
|
+
current_datetime: pureCompute(),
|
|
23961
|
+
dice_roll: pureCompute(),
|
|
23962
|
+
prompt_enhancement: pureCompute(),
|
|
23963
|
+
find_definition: {
|
|
23964
|
+
source: CLI,
|
|
23965
|
+
effect: "read",
|
|
23966
|
+
approval: "auto_approve"
|
|
23967
|
+
},
|
|
23968
|
+
ask_user_question: {
|
|
23969
|
+
source: CORE,
|
|
23970
|
+
effect: "session",
|
|
23971
|
+
approval: "auto_approve"
|
|
23972
|
+
},
|
|
23973
|
+
agent_delegate: sessionTool(),
|
|
23974
|
+
check_agent_status: sessionTool(),
|
|
23975
|
+
list_background_agents: sessionTool(),
|
|
23976
|
+
cancel_background_agent: sessionTool(),
|
|
23977
|
+
resume_agent: sessionTool(),
|
|
23978
|
+
create_dynamic_agent: sessionTool(),
|
|
23979
|
+
coordinate_task: sessionTool(),
|
|
23980
|
+
decompose_task: sessionTool(),
|
|
23981
|
+
shared_context_read: sessionTool(),
|
|
23982
|
+
shared_context_write: sessionTool(),
|
|
23983
|
+
write_todos: sessionTool(),
|
|
23984
|
+
read_prior_todos: sessionTool(),
|
|
23985
|
+
log_decision: sessionTool(),
|
|
23986
|
+
track_blocker: sessionTool(),
|
|
23987
|
+
resolve_blocker: sessionTool(),
|
|
23988
|
+
request_review_gate: sessionTool(),
|
|
23989
|
+
work_item_create: sessionTool(),
|
|
23990
|
+
work_item_update: sessionTool(),
|
|
23991
|
+
work_item_close: sessionTool(),
|
|
23992
|
+
work_item_ready: sessionTool(),
|
|
23993
|
+
work_item_graph: sessionTool(),
|
|
23994
|
+
tool_search: sessionTool(),
|
|
23995
|
+
create_file: fileWriter(),
|
|
23996
|
+
edit_local_file: fileWriter(),
|
|
23997
|
+
delete_file: fileWriter(),
|
|
23998
|
+
edit_file: {
|
|
23999
|
+
source: CORE,
|
|
24000
|
+
effect: "write",
|
|
24001
|
+
approval: "prompt_always"
|
|
24002
|
+
},
|
|
24003
|
+
bash_execute: {
|
|
24004
|
+
source: CORE,
|
|
24005
|
+
effect: "execute",
|
|
24006
|
+
approval: "prompt_always",
|
|
24007
|
+
openWorld: true,
|
|
24008
|
+
commandArg: "command"
|
|
24009
|
+
},
|
|
24010
|
+
shell_execute: {
|
|
24011
|
+
source: CORE,
|
|
24012
|
+
effect: "execute",
|
|
24013
|
+
approval: "prompt_always",
|
|
24014
|
+
openWorld: true
|
|
24015
|
+
},
|
|
24016
|
+
write_shell_stdin: {
|
|
24017
|
+
source: CORE,
|
|
24018
|
+
effect: "execute",
|
|
24019
|
+
approval: "prompt_always",
|
|
24020
|
+
commandArg: "chars"
|
|
24021
|
+
},
|
|
24022
|
+
kill_background_shell: {
|
|
24023
|
+
source: CORE,
|
|
24024
|
+
effect: "execute",
|
|
24025
|
+
approval: "prompt_always"
|
|
24026
|
+
},
|
|
24027
|
+
git_commit: {
|
|
24028
|
+
source: CORE,
|
|
24029
|
+
effect: "write",
|
|
24030
|
+
approval: "prompt_always"
|
|
24031
|
+
},
|
|
24032
|
+
git_push: {
|
|
24033
|
+
source: CORE,
|
|
24034
|
+
effect: "write",
|
|
24035
|
+
approval: "prompt_always",
|
|
24036
|
+
openWorld: true
|
|
24037
|
+
},
|
|
24038
|
+
skill: {
|
|
24039
|
+
source: CLI,
|
|
24040
|
+
effect: "execute",
|
|
24041
|
+
approval: "prompt_always"
|
|
24042
|
+
},
|
|
24043
|
+
weather_info: reader(CORE, { openWorld: true }),
|
|
24044
|
+
web_search: reader(CORE, { openWorld: true }),
|
|
24045
|
+
web_fetch: reader(CORE, { openWorld: true }),
|
|
24046
|
+
deep_research: reader(CORE, { openWorld: true }),
|
|
24047
|
+
check_shell_output: reader(),
|
|
24048
|
+
list_background_shells: reader(),
|
|
24049
|
+
file_read: reader(),
|
|
24050
|
+
grep_search: reader(),
|
|
24051
|
+
glob_files: reader(),
|
|
24052
|
+
get_file_tree: reader(),
|
|
24053
|
+
get_file_structure: reader(CLI),
|
|
24054
|
+
recent_changes: reader(),
|
|
24055
|
+
git_status: reader(),
|
|
24056
|
+
git_diff: reader(),
|
|
24057
|
+
git_log: reader(),
|
|
24058
|
+
git_branch: reader(),
|
|
24059
|
+
lattice_create_model: lattice(),
|
|
24060
|
+
lattice_add_entity: lattice(),
|
|
24061
|
+
lattice_set_value: lattice(),
|
|
24062
|
+
lattice_create_rule: lattice(),
|
|
24063
|
+
lattice_query: lattice(),
|
|
24064
|
+
lattice_explain: lattice()
|
|
24065
|
+
}));
|
|
24066
|
+
//#endregion
|
|
24067
|
+
//#region src/tools/gateway/toolPolicy.ts
|
|
23797
24068
|
/**
|
|
23798
|
-
*
|
|
24069
|
+
* Every policy decision about a tool, derived from its descriptor. `undefined` is a tool the
|
|
24070
|
+
* catalog could not resolve; it keeps the pre-descriptor fallback (prompt_default, read-only)
|
|
24071
|
+
* because feature-module and plugin tools do not register descriptors yet.
|
|
23799
24072
|
*/
|
|
23800
|
-
const
|
|
23801
|
-
|
|
23802
|
-
"
|
|
23803
|
-
"
|
|
24073
|
+
const UNKNOWN_TOOL_CATEGORY = "prompt_default";
|
|
24074
|
+
const READ_ONLY_EFFECTS = /* @__PURE__ */ new Set([
|
|
24075
|
+
"none",
|
|
24076
|
+
"session",
|
|
24077
|
+
"read"
|
|
23804
24078
|
]);
|
|
23805
|
-
z$1.object({
|
|
23806
|
-
categories: z$1.record(z$1.string(), ToolCategorySchema),
|
|
23807
|
-
trustedTools: z$1.array(z$1.string())
|
|
23808
|
-
});
|
|
23809
|
-
/**
|
|
23810
|
-
* Default tool categories
|
|
23811
|
-
*
|
|
23812
|
-
* Categories:
|
|
23813
|
-
* - auto_approve: Safe tools that don't need permission (math, search, datetime)
|
|
23814
|
-
* - prompt_always: Dangerous tools that ALWAYS need permission, cannot be trusted (file edits, shell commands)
|
|
23815
|
-
* - prompt_default: Tools that prompt by default but users can trust them (file reads, searches)
|
|
23816
|
-
*/
|
|
23817
|
-
const DEFAULT_TOOL_CATEGORIES = {
|
|
23818
|
-
math_evaluate: "auto_approve",
|
|
23819
|
-
current_datetime: "auto_approve",
|
|
23820
|
-
dice_roll: "auto_approve",
|
|
23821
|
-
prompt_enhancement: "auto_approve",
|
|
23822
|
-
find_definition: "auto_approve",
|
|
23823
|
-
ask_user_question: "auto_approve",
|
|
23824
|
-
weather_info: "prompt_default",
|
|
23825
|
-
agent_delegate: "auto_approve",
|
|
23826
|
-
check_agent_status: "auto_approve",
|
|
23827
|
-
list_background_agents: "auto_approve",
|
|
23828
|
-
cancel_background_agent: "auto_approve",
|
|
23829
|
-
resume_agent: "auto_approve",
|
|
23830
|
-
create_dynamic_agent: "auto_approve",
|
|
23831
|
-
coordinate_task: "auto_approve",
|
|
23832
|
-
decompose_task: "auto_approve",
|
|
23833
|
-
shared_context_read: "auto_approve",
|
|
23834
|
-
shared_context_write: "auto_approve",
|
|
23835
|
-
write_todos: "auto_approve",
|
|
23836
|
-
read_prior_todos: "auto_approve",
|
|
23837
|
-
log_decision: "auto_approve",
|
|
23838
|
-
track_blocker: "auto_approve",
|
|
23839
|
-
resolve_blocker: "auto_approve",
|
|
23840
|
-
request_review_gate: "auto_approve",
|
|
23841
|
-
work_item_create: "auto_approve",
|
|
23842
|
-
work_item_update: "auto_approve",
|
|
23843
|
-
work_item_close: "auto_approve",
|
|
23844
|
-
work_item_ready: "auto_approve",
|
|
23845
|
-
work_item_graph: "auto_approve",
|
|
23846
|
-
tool_search: "auto_approve",
|
|
23847
|
-
edit_file: "prompt_always",
|
|
23848
|
-
edit_local_file: "prompt_always",
|
|
23849
|
-
create_file: "prompt_always",
|
|
23850
|
-
delete_file: "prompt_always",
|
|
23851
|
-
shell_execute: "prompt_always",
|
|
23852
|
-
bash_execute: "prompt_always",
|
|
23853
|
-
write_shell_stdin: "prompt_always",
|
|
23854
|
-
kill_background_shell: "prompt_always",
|
|
23855
|
-
git_commit: "prompt_always",
|
|
23856
|
-
git_push: "prompt_always",
|
|
23857
|
-
skill: "prompt_always",
|
|
23858
|
-
web_search: "prompt_default",
|
|
23859
|
-
check_shell_output: "prompt_default",
|
|
23860
|
-
list_background_shells: "prompt_default",
|
|
23861
|
-
web_fetch: "prompt_default",
|
|
23862
|
-
deep_research: "prompt_default",
|
|
23863
|
-
file_read: "prompt_default",
|
|
23864
|
-
grep_search: "prompt_default",
|
|
23865
|
-
glob_files: "prompt_default",
|
|
23866
|
-
get_file_tree: "prompt_default",
|
|
23867
|
-
get_file_structure: "prompt_default",
|
|
23868
|
-
git_status: "prompt_default",
|
|
23869
|
-
git_diff: "prompt_default",
|
|
23870
|
-
git_log: "prompt_default",
|
|
23871
|
-
git_branch: "prompt_default"
|
|
23872
|
-
};
|
|
23873
24079
|
/**
|
|
23874
|
-
*
|
|
23875
|
-
*
|
|
23876
|
-
|
|
23877
|
-
|
|
23878
|
-
|
|
23879
|
-
|
|
23880
|
-
|
|
23881
|
-
|
|
23882
|
-
|
|
24080
|
+
* How far a source's own declarations are believed. Core, cli and builtin-module tools ship in
|
|
24081
|
+
* this repo and are reviewed, so their descriptor is taken as written. A plugin is third-party
|
|
24082
|
+
* code the user installed and enabled: it can be trusted per tool after a prompt, but cannot
|
|
24083
|
+
* skip the first prompt or claim to be read-only. MCP tools and lifecycle hooks always prompt.
|
|
24084
|
+
*/
|
|
24085
|
+
function categoryCeiling(source) {
|
|
24086
|
+
switch (source.kind) {
|
|
24087
|
+
case "mcp":
|
|
24088
|
+
case "hook": return "prompt_always";
|
|
24089
|
+
case "plugin": return "prompt_default";
|
|
24090
|
+
default: return;
|
|
24091
|
+
}
|
|
24092
|
+
}
|
|
24093
|
+
function believesDeclaredEffect(source) {
|
|
24094
|
+
return categoryCeiling(source) === void 0;
|
|
24095
|
+
}
|
|
24096
|
+
function toolCategory(descriptor) {
|
|
24097
|
+
if (!descriptor) return UNKNOWN_TOOL_CATEGORY;
|
|
24098
|
+
const ceiling = categoryCeiling(descriptor.source);
|
|
24099
|
+
if (ceiling === "prompt_always") return ceiling;
|
|
24100
|
+
if (ceiling === "prompt_default" && descriptor.approval === "auto_approve") return ceiling;
|
|
24101
|
+
return descriptor.approval;
|
|
24102
|
+
}
|
|
24103
|
+
/** Whether a prompt_default trust decision can be persisted for the tool. */
|
|
24104
|
+
function canTrustTool(descriptor) {
|
|
24105
|
+
return toolCategory(descriptor) !== "prompt_always";
|
|
24106
|
+
}
|
|
24107
|
+
/** Read-only tools run in plan mode and in parallel batches; the rest are blocked / sequential. */
|
|
24108
|
+
function isReadOnlyTool(descriptor) {
|
|
24109
|
+
if (!descriptor) return true;
|
|
24110
|
+
if (!believesDeclaredEffect(descriptor.source)) return false;
|
|
24111
|
+
return READ_ONLY_EFFECTS.has(descriptor.effect);
|
|
24112
|
+
}
|
|
24113
|
+
/** The shell command text the risk gate classifies, when the tool has one in `args`. */
|
|
24114
|
+
function commandTextOf(descriptor, args) {
|
|
24115
|
+
return stringArg(args, descriptor?.commandArg);
|
|
24116
|
+
}
|
|
24117
|
+
/** The path a file-writing tool will write, when the tool declares one and `args` carries it. */
|
|
24118
|
+
function writePathOf(descriptor, args) {
|
|
24119
|
+
const writePath = stringArg(args, descriptor?.writePathArg);
|
|
24120
|
+
return writePath ? writePath : void 0;
|
|
24121
|
+
}
|
|
24122
|
+
function stringArg(args, field) {
|
|
24123
|
+
if (!field || args === null || typeof args !== "object") return void 0;
|
|
24124
|
+
const value = args[field];
|
|
24125
|
+
return typeof value === "string" ? value : void 0;
|
|
24126
|
+
}
|
|
24127
|
+
//#endregion
|
|
24128
|
+
//#region src/tools/gateway/toolCatalog.ts
|
|
24129
|
+
const MCP_PREFIX = "mcp__";
|
|
24130
|
+
const HOOK_PREFIXES = ["agent_hook:", "skill_hook:"];
|
|
24131
|
+
function isHookName(toolName) {
|
|
24132
|
+
return HOOK_PREFIXES.some((prefix) => toolName.startsWith(prefix));
|
|
23883
24133
|
}
|
|
23884
24134
|
/**
|
|
23885
|
-
*
|
|
23886
|
-
|
|
23887
|
-
|
|
23888
|
-
|
|
24135
|
+
* Resolve a tool name without a catalog instance: the core table, then the MCP namespace
|
|
24136
|
+
* (`mcp__<server>__<tool>`), then the lifecycle-hook namespaces. Undefined means unknown.
|
|
24137
|
+
* ToolCatalog.resolve falls back to this after its registered descriptors.
|
|
24138
|
+
*/
|
|
24139
|
+
function resolveStaticToolDescriptor(toolName) {
|
|
24140
|
+
const core = CORE_TOOL_DESCRIPTORS.get(toolName);
|
|
24141
|
+
if (core) return core;
|
|
24142
|
+
if (toolName.startsWith(MCP_PREFIX)) return {
|
|
24143
|
+
source: {
|
|
24144
|
+
kind: "mcp",
|
|
24145
|
+
server: toolName.slice(5).split("__")[0]
|
|
24146
|
+
},
|
|
24147
|
+
effect: "execute",
|
|
24148
|
+
approval: "prompt_always"
|
|
24149
|
+
};
|
|
24150
|
+
if (isHookName(toolName)) return {
|
|
24151
|
+
source: { kind: "hook" },
|
|
24152
|
+
effect: "execute",
|
|
24153
|
+
approval: "prompt_always"
|
|
24154
|
+
};
|
|
23889
24155
|
}
|
|
23890
24156
|
/**
|
|
23891
|
-
*
|
|
23892
|
-
*
|
|
23893
|
-
*
|
|
23894
|
-
* @param toolName - Name of the tool to check
|
|
23895
|
-
* @param customCategories - Optional custom category overrides
|
|
23896
|
-
* @returns true if the tool is read-only, false if it's a write tool
|
|
24157
|
+
* Name -> descriptor lookup for one CLI process or ACP stack. Entry points create one and hand
|
|
24158
|
+
* it to their PermissionManager, which the permission gateway and the agent's read-only
|
|
24159
|
+
* predicate read it through.
|
|
23897
24160
|
*/
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
|
|
24161
|
+
var ToolCatalog = class {
|
|
24162
|
+
constructor() {
|
|
24163
|
+
this.registered = /* @__PURE__ */ new Map();
|
|
24164
|
+
}
|
|
24165
|
+
/**
|
|
24166
|
+
* Add a descriptor for a tool the core table does not know. Core names and the MCP and hook
|
|
24167
|
+
* namespaces are reserved: nothing can re-declare a core tool, or declare itself into a
|
|
24168
|
+
* namespace whose descriptor comes from the name alone, with a lower tier.
|
|
24169
|
+
*/
|
|
24170
|
+
register(toolName, descriptor) {
|
|
24171
|
+
if (toolName.startsWith(MCP_PREFIX) || isHookName(toolName)) throw new Error(`Tool name "${toolName}" is in a reserved namespace and cannot be registered`);
|
|
24172
|
+
if (CORE_TOOL_DESCRIPTORS.has(toolName)) throw new Error(`Tool "${toolName}" is a core tool and already has a descriptor`);
|
|
24173
|
+
if (this.registered.has(toolName)) throw new Error(`Tool "${toolName}" already has a registered descriptor`);
|
|
24174
|
+
this.registered.set(toolName, descriptor);
|
|
24175
|
+
}
|
|
24176
|
+
resolve(toolName) {
|
|
24177
|
+
return this.registered.get(toolName) ?? resolveStaticToolDescriptor(toolName);
|
|
24178
|
+
}
|
|
24179
|
+
category(toolName) {
|
|
24180
|
+
return toolCategory(this.resolve(toolName));
|
|
24181
|
+
}
|
|
24182
|
+
canTrust(toolName) {
|
|
24183
|
+
return canTrustTool(this.resolve(toolName));
|
|
24184
|
+
}
|
|
24185
|
+
isReadOnly(toolName) {
|
|
24186
|
+
return isReadOnlyTool(this.resolve(toolName));
|
|
24187
|
+
}
|
|
24188
|
+
};
|
|
23901
24189
|
const PROJECT_CONTEXT_FILES = [
|
|
23902
24190
|
"CLAUDE.local.md",
|
|
23903
24191
|
"CLAUDE.md",
|
|
@@ -24638,7 +24926,7 @@ function mergeConfigs(global, project, local) {
|
|
|
24638
24926
|
enabled: merged.tools.enabled.filter((t) => !disabledSet.has(t))
|
|
24639
24927
|
};
|
|
24640
24928
|
const globalTrusted = new Set(global.trustedTools || []);
|
|
24641
|
-
merged.trustedTools = (merged.trustedTools || []).filter((t) => globalTrusted.has(t) || canTrustTool(t) && !disabledSet.has(t));
|
|
24929
|
+
merged.trustedTools = (merged.trustedTools || []).filter((t) => globalTrusted.has(t) || canTrustTool(resolveStaticToolDescriptor(t)) && !disabledSet.has(t));
|
|
24642
24930
|
return merged;
|
|
24643
24931
|
}
|
|
24644
24932
|
/**
|
|
@@ -25315,4 +25603,4 @@ var ConfigStore = class {
|
|
|
25315
25603
|
}
|
|
25316
25604
|
};
|
|
25317
25605
|
//#endregion
|
|
25318
|
-
export {
|
|
25606
|
+
export { PermissionDeniedError as $, ApiKeyType as A, stripToolArtifactMarkup as At, DEFAULT_TTS_PROVIDER as B, actorKindMarker as Bt, htmlFirstH1 as C, obfuscateApiKey as Ct, AGENT_QUEST_MCP_URI as D, signImageUrl as Dt, AGENT_QUEST_MANIFEST as E, secureParameters as Et, CONTEXT_WINDOW_SAFETY_BUFFER_TOKENS as F, ttsResponseTooLargeSchema as Ft, LOCATION_MAP_LANGUAGE as G, WEBSITE_URL as Gt, GENERATED_IMAGE_EXTENSION_RE as H, delegationPayloadSchema as Ht, CREDIT_DEDUCT_TRANSACTION_TYPES as I, withRetry as It, ModelBackend as J, MODEL_INFO_FIELD_GROUP_OF as K, ChatModels as L, ACTOR_COLOR_SLOTS as Lt, BRIEFCASE_CATALOG_QUERIES as M, tokenEstimateMultiplier as Mt, BedrockEmbeddingModel as N, ttsBase64ResponseSchema as Nt, ARTIFACT_DELIVERED_PLACEHOLDER as O, splitEquals as Ot, BriefcasePromptIdSchema as P, ttsRequestSchema as Pt, PROMPT_TEXT_MAX as Q, CompletionReasoningEffortSchema as R, DELEGATION_PAYLOAD_SCHEMA_NAME as Rt, htmlErrorTitle as S, joinReplySlots as St, AGENT_QUEST_ID as T, replacePromptVariables as Tt, ImageModels as U, selfClaimedActorKindSchema as Ut, DEGENERATE_FINISH_REASON as V, actorKindSchema as Vt, ImagePromptResolutionSchema as W, APP_NAME as Wt, OllamaEmbeddingModel as X, NotFoundError as Y, OpenAIEmbeddingModel as Z, NotAuthenticatedError as _, hasDeliverablePinnedArtifact as _t, commandTextOf as a, VisibilitySchema as at, SESSION_ID_PATTERN as b, isRetryableError as bt, writePathOf as c, assertAuthOriginMatchesApiKey as ct, getEnvironmentName as d, escapeThinkMarkers as dt, REVIEW_GATE_STATUS_VALUES as et, requireApiUrl as f, filterToolArtifactMarkup as ft, LOCAL_DEV_URL as g, googleMapsSearchUrl as gt, DeviceFlowClient as h, getQuestErrorCode as ht, ToolCatalog as i, SupportedFabFileMimeTypes as it, ArtifactTypeSchema as j, supportedVoiceGenerationVendor as jt, ARTIFACT_REMOVED_PLACEHOLDER as k, stripDeliveredArtifactBlocks as kt, ApiEndpointUnconfiguredError as l, buildPromptContext as lt, AuthenticatedApiClient as m, getMcpProviderMetadata as mt, extractCompactInstructions as n, SLUG_OWNED_BY_OTHER as nt, isReadOnlyTool as o, VoyageAIEmbeddingModel as ot, resolveApiEndpoint as p, generatedAudioResponseSchema as pt, McpServerName as q, loadContextFiles as r, SUBQUEST_STATUS_VALUES as rt, toolCategory as s, WORK_ITEM_STATUSES as st, ConfigStore as t, SEARCH_RESULT_CARDS_LANGUAGE as tt, getCreditsUrl as u, createThinkMarkerEscaper as ut, parseApiUrl as v, isEarlyStop as vt, escapeArtifactBodyJson as w, questReplyText as wt, isValidSessionId as x, isUserInitiatedAbort as xt, logger as y, isPlaceholderImageSigningSecret as yt, CreateArtifactRequestSchema as z, actorColorIndex as zt };
|