修复 agc_tools 媒体资源提示词上限并按 kind 暴露 (#398)
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (push) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
## 改动 - 提示词上限收敛为 `resource_edit_prompt_max_chars` 单一权威:`agc_tools` MCP 工具层、客户端受控工具桥与提交校验共用同一数字与拒绝文案,不再出现「schema 写 4000、真实上限按 kind 分」的分叉。 - `agc_create_or_derive_resource` 的 schema 逐 kind 声明 `prompt.maxLength`(background-music / sound-effect / video 与 character-animation),`prompt` 描述写明每个数字;`agc_edit_image` 继续用图片口径 32000。 - 工具桥不再用通用 4000 校验 prompt:图片编辑 4000~32000 的提示词不再被误报成「超出安全边界」。 - 源资源未登记时的错误文案补上可执行两步:先用 `agc_list_registered_assets` 选已有 localAssetId;只在项目里的文件先 `agc_list_project_files` 确认 `assetImportable=true`,再用 `agc_import_account_assets.localPaths` 登记后重试。`agc_remove_background` 复用同一条文案。 - skill 包 `agc-client-projection` 的 SKILL.md 与 projection-contract.md 写明四个上限,并说明超限要在本地收敛而不是原样重发;清单指纹同步到 `2026-08-26.18`(已合并 master 的抠图文案)。 - 前端 `resourceEditModel.ts` 补注释指向 Rust 权威函数,数字未动。 - decision-log、pitfalls 与《AI游戏创作智能体App实施计划》记录本轮口径;未改 External v1 契约 / OpenAPI、SpacetimeDB schema 与客户端 UI 行为。 ## 问题 | 编号 | 问题 | 本 PR 状态 | | --- | --- | --- | | P1 | 背景音乐提示词真实上限 140,但 schema 只写 4000、skill 未写明,agent 写一句正常长度描述必被硬拒 | 已修复:口径单源 + 逐 kind 暴露 + skill 写明 | | P2 | 音效 1900 与图片编辑 32000 的上限只在 Rust 内部生效,MCP 与工具桥各自硬编码或误报 | 已修复:三处共用同一函数,图片编辑不再误报安全边界 | | P3 | 源资源未登记时只报「不属于当前项目已登记资源」,模型会原地重试 | 已修复:文案给出 `agc_list_registered_assets` 与登记工具两步动作 | | P4 | 这两个工具此前没有端到端用例,同类漂移无法被门禁拦住 | 已补:MCP 工具层 → 真实工具桥 → 假平台的契约用例 + 两条按 kind 上限门禁 | ## 验证 - `agent::direct_tools_mcp::tests` 23 passed / 0 failed:含新增的按 kind 上限门禁、图片快速编辑契约(断言 `POST /api/editor/images/edits` 的路径、Bearer、Idempotency-Key、正文且不得回填 assetKind)、背景音乐契约(断言 `POST /api/editor/audios/background-music/generations` 的 `gptDescriptionPrompt` 与 `makeInstrumental`)、超限零桥请求、未登记源资源提示。 - `agent::direct_tool_bridge::tests` 18 passed / 7 failed;`agent::skill_pack` 4 passed;`npm run agc:skill-pack:check` 与 `skill-pack:test` 通过(version `2026-08-26.18`)。 - AGC `typecheck`(tsc + skill-pack check + check-config)、`resourceEditModel` 6 项前端测试、`npm run check:encoding`、`npm run check:doc-index`、`cargo fmt --check`、`git diff --check` 通过。 - 那 7 条(以及 `project::resource_editor` 45 条)失败与本 PR 无关:全部是 `tempfile::tempdir()` 的 `Windows 安全对象不属于当前用户`,已 stash 改动并用基线复跑确认失败集合一致。 - 未跑整包 AGC Rust 全量分片、`npm run lint` 聚合门禁与 CI;未做任何付费生成。工具 schema 与 skill 文案都在应用内,终端用户需重新构建客户端才能看到修复效果。 --------- Co-authored-by: kdletters <61648117+kdletters@users.noreply.github.com> Reviewed-on: #398
This commit was merged in pull request #398.
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@ Let the client derive projections from real disk changes and trusted tool result
|
||||
2. Before using or deriving an existing registered asset, call `agc_list_registered_assets` and select its `localAssetId`. If the user points to an existing project file that is not listed, first call `agc_list_project_files`; only entries with `assetImportable=true` (recognized image, font, audio, video, document, or code files) may be passed to `agc_import_account_assets.localPaths`. Then re-read `agc_list_registered_assets`; never infer a source identity from a filename or fabricate a localAssetId.
|
||||
3. Keep read scopes separate: `asset.list` is the current project manifest, `asset.library.list` is the signed-in account library, and the web project's canvas resource read model is the authoritative canvas list. The account library is not the complete canvas list.
|
||||
4. Use `canvas.asset_import` for safe account/canvas asset IDs or project-relative local paths. The client rechecks ownership and validates bytes; host absolute paths require native UI file-picker authorization.
|
||||
5. When the user explicitly asks to create or derive video, character animation, sound effect, or background music, call `agc_create_or_derive_resource`. Use `create` only for video/audio without a source and `derive` with a registered `sourceLocalAssetId`; character animation is always derived from an image.
|
||||
5. When the user explicitly asks to create or derive video, character animation, sound effect, or background music, call `agc_create_or_derive_resource`. Use `create` only for video/audio without a source and `derive` with a registered `sourceLocalAssetId`; character animation is always derived from an image. Keep `prompt` inside the per-kind limit that the client really enforces: background music at most 140 characters, sound effect at most 1900, video and character animation at most 4000. A longer prompt is rejected before submission, so write the short version first instead of retrying the same text.
|
||||
6. When the user explicitly asks to remove an image background, call `agc_remove_background` with a registered image `sourceLocalAssetId` and `assetName`. Optional `backgroundMode` is `complex` (semantic foreground segmentation; default) or `flat` (solid-colour background removal). Prefer `flat` when the background is known to be solid. Only `flat` accepts optional `screenColor`: `auto`, `#RRGGBB`, or omitted for automatic detection by the service. Do not select a colour on behalf of `auto`. The client requires the signed-in account, owns canvas/folder context and task identity, and returns only bounded queue state.
|
||||
7. Preserve existing relative paths when a small edit is sufficient so client resource identities remain stable.
|
||||
8. Do not edit `.agent/manifest.json`, revision counters, version records, resource IDs, canvas identities, source provenance, generation ledgers, or browser receipts by hand.
|
||||
|
||||
+2
@@ -14,4 +14,6 @@ Read scopes remain separate: `asset.list` is the current project's local manifes
|
||||
|
||||
`agc_create_or_derive_resource` accepts only semantic intent. The client resolves `sourceLocalAssetId`, creates stable request identities, recovers matching pending operations, serializes paid submissions, writes supported media into the current canvas and same-name asset folder, validates downloaded bytes, commits the local manifest transaction, and returns redacted warnings. A tool error or timeout is not permission to generate again with a new identity.
|
||||
|
||||
`prompt` limits are per kind and are enforced before any paid submission: background music accepts 1-140 characters, sound effect 1-1900, video and character animation 1-4000, and image editing (`agc_edit_image`) 1-32000. The client composes the submitted request from a fixed prefix plus your prompt, so an over-limit prompt fails locally with the exact limit; shorten the text rather than resubmitting the same value. `agc_edit_image` remains the image path; this tool never generates or edits still images.
|
||||
|
||||
`agc_remove_background` accepts a registered image `sourceLocalAssetId`, `assetName`, and optional `backgroundMode` and `screenColor`. `complex` uses semantic segmentation to identify the foreground; `flat` removes a solid-colour background. Prefer `flat` when the background is known to be solid; omitting the mode selects `complex`. Only `flat` accepts a colour: `auto`, `#RRGGBB`, or omitted for automatic service detection. Never infer a concrete colour for `auto`. Empty or invalid values and colour without `flat` are rejected. The client resolves the formal source resource, canvas/folder context, stable operation identity, idempotency key, and authenticated External v1 `/api/external/v1/editor/images/background-removals` call. Mode and colour are part of request identity. Its result is bounded queue state; Codex must not poll internal workers, construct source URLs, or retry with a new identity after an uncertain response.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": "agc-skill-pack.v1",
|
||||
"version": "2026-08-26.17",
|
||||
"version": "2026-08-26.18",
|
||||
"skills": [
|
||||
{
|
||||
"name": "agc-game-production-workflow",
|
||||
@@ -123,7 +123,7 @@
|
||||
"agents/openai.yaml",
|
||||
"references/projection-contract.md"
|
||||
],
|
||||
"sha256": "a929c27bc5b2b0bee0b7935e5c7b04ddbab1eb1804fe196f8c2537ad040ca5b1"
|
||||
"sha256": "0700d4a7a18ee6151811f38786211ad416863f2e425fdc2ded67555a0a1923a1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ const DIRECT_TOOL_BRIDGE_IMAGE_PREVIEW_MAX_DIMENSION: u32 = 1024;
|
||||
const DIRECT_TOOL_BRIDGE_MAX_SEARCH_QUERY_CHARS: usize = 400;
|
||||
const DIRECT_TOOL_BRIDGE_MAX_SEARCH_RESULTS: usize = 5;
|
||||
const DIRECT_TOOL_BRIDGE_SEARCH_URL: &str = "https://www.bing.com/search?format=rss";
|
||||
const DIRECT_TOOL_BRIDGE_MAX_RESOURCE_PROMPT_CHARS: usize = 4_000;
|
||||
const DIRECT_TOOL_BRIDGE_MAX_RESOURCE_NAME_CHARS: usize = 120;
|
||||
const DIRECT_TOOL_BRIDGE_MAX_RESOURCE_KIND_CHARS: usize = 80;
|
||||
const DIRECT_TOOL_BRIDGE_MAX_RESOURCE_PAGE_SIZE: usize = 100;
|
||||
@@ -89,7 +88,7 @@ struct DirectToolBridgeRequest {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum DirectResourceGenerationKind {
|
||||
pub(crate) enum DirectResourceGenerationKind {
|
||||
Image,
|
||||
Video,
|
||||
CharacterAnimation,
|
||||
@@ -98,7 +97,7 @@ enum DirectResourceGenerationKind {
|
||||
}
|
||||
|
||||
impl DirectResourceGenerationKind {
|
||||
fn parse(value: &str) -> Result<Self, String> {
|
||||
pub(crate) fn parse(value: &str) -> Result<Self, String> {
|
||||
match value {
|
||||
"image" => Ok(Self::Image),
|
||||
"video" => Ok(Self::Video),
|
||||
@@ -119,7 +118,7 @@ impl DirectResourceGenerationKind {
|
||||
}
|
||||
}
|
||||
|
||||
fn edit_kind(self) -> LocalProjectResourceEditKind {
|
||||
pub(crate) fn edit_kind(self) -> LocalProjectResourceEditKind {
|
||||
match self {
|
||||
Self::Image => LocalProjectResourceEditKind::ImageReference,
|
||||
Self::Video => LocalProjectResourceEditKind::Video,
|
||||
@@ -128,6 +127,11 @@ impl DirectResourceGenerationKind {
|
||||
Self::BackgroundMusic => LocalProjectResourceEditKind::BackgroundMusic,
|
||||
}
|
||||
}
|
||||
|
||||
/// 提示词上限只从客户端权威口径取值,工具桥与 MCP 层共用同一份数字。
|
||||
pub(crate) fn prompt_max_chars(self) -> usize {
|
||||
resource_edit_prompt_max_chars(&self.edit_kind())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
@@ -1036,6 +1040,12 @@ fn bridge_account_asset_import_inputs(
|
||||
Ok((asset_ids, local_paths))
|
||||
}
|
||||
|
||||
/// 源资源身份不在当前项目 manifest 时的统一提示。
|
||||
///
|
||||
/// 只报「不属于已登记资源」会让模型原地重试;这里必须把下一步可执行动作写清楚:
|
||||
/// 已登记资源走 `agc_list_registered_assets`,只在项目里存在的文件先登记再重试。
|
||||
const DIRECT_TOOL_BRIDGE_UNREGISTERED_SOURCE_MESSAGE: &str = "sourceLocalAssetId 不是当前项目已登记资源:先调用 agc_list_registered_assets 选择已有 localAssetId;若目标图片只在项目里,先用 agc_list_project_files 确认它 assetImportable=true,再用 agc_import_account_assets.localPaths 登记后重试。";
|
||||
|
||||
fn bridge_resource_generation_input(
|
||||
arguments: &Value,
|
||||
) -> Result<DirectResourceGenerationInput, String> {
|
||||
@@ -1054,18 +1064,20 @@ fn bridge_resource_generation_input(
|
||||
"sourceLocalAssetId",
|
||||
DIRECT_TOOL_BRIDGE_MAX_RESOURCE_KIND_CHARS,
|
||||
)?;
|
||||
let prompt = bridge_bounded_string(
|
||||
arguments,
|
||||
"prompt",
|
||||
DIRECT_TOOL_BRIDGE_MAX_RESOURCE_PROMPT_CHARS,
|
||||
)?;
|
||||
// prompt 的形状校验只用信封级上限,真正生效的按 kind 上限由紧随其后的权威判定给出
|
||||
// 精确数字;否则通用 4000 会先于「图片编辑 32000 / 音效 1900」误报成安全边界错误。
|
||||
let prompt = bridge_bounded_string(arguments, "prompt", DIRECT_TOOL_BRIDGE_MAX_REQUEST_BYTES)?;
|
||||
let asset_name = bridge_bounded_string(
|
||||
arguments,
|
||||
"assetName",
|
||||
DIRECT_TOOL_BRIDGE_MAX_RESOURCE_NAME_CHARS,
|
||||
)?;
|
||||
if kind == DirectResourceGenerationKind::BackgroundMusic && prompt.chars().count() > 140 {
|
||||
return Err("背景音乐提示词必须在 1..=140 字符内".to_string());
|
||||
let prompt_max_chars = kind.prompt_max_chars();
|
||||
if prompt.chars().count() > prompt_max_chars {
|
||||
return Err(resource_edit_prompt_limit_error(
|
||||
&kind.edit_kind(),
|
||||
prompt_max_chars,
|
||||
));
|
||||
}
|
||||
match (kind, mode, source_local_asset_id.as_ref()) {
|
||||
(DirectResourceGenerationKind::Image, DirectResourceGenerationMode::Create, _) => {
|
||||
@@ -1806,7 +1818,7 @@ async fn bridge_create_or_derive_resource(
|
||||
.iter()
|
||||
.find(|asset| asset.id == asset_id)
|
||||
.cloned()
|
||||
.ok_or_else(|| "sourceLocalAssetId 不属于当前项目已登记资源".to_string())
|
||||
.ok_or_else(|| DIRECT_TOOL_BRIDGE_UNREGISTERED_SOURCE_MESSAGE.to_string())
|
||||
})
|
||||
.transpose()?;
|
||||
let prompt_sha256 = format!("{:x}", Sha256::digest(input.prompt.as_bytes()));
|
||||
@@ -1903,7 +1915,7 @@ async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Val
|
||||
.assets
|
||||
.iter()
|
||||
.find(|asset| asset.id == source_asset_id)
|
||||
.ok_or_else(|| "sourceLocalAssetId 不属于当前项目已登记资源".to_string())?;
|
||||
.ok_or_else(|| DIRECT_TOOL_BRIDGE_UNREGISTERED_SOURCE_MESSAGE.to_string())?;
|
||||
if !source_asset.media_type.starts_with("image/") {
|
||||
return Err("抠图工具只接受当前项目已登记的图片资源".to_string());
|
||||
}
|
||||
@@ -2795,6 +2807,67 @@ mod tests {
|
||||
.contains("x-genarrative-client:"));
|
||||
}
|
||||
|
||||
/// 按 kind 的提示词上限只来自客户端权威口径;超限必须在构造工具输入时就被拒绝,
|
||||
/// 不能再出现写死的数字(2026-09-17 的背景音乐 140 就是写死在桥这一层的)。
|
||||
#[test]
|
||||
fn bridge_resource_prompt_limits_follow_the_client_authority() {
|
||||
for (kind, edit_kind) in [
|
||||
(
|
||||
"background-music",
|
||||
LocalProjectResourceEditKind::BackgroundMusic,
|
||||
),
|
||||
("sound-effect", LocalProjectResourceEditKind::SoundEffect),
|
||||
("video", LocalProjectResourceEditKind::Video),
|
||||
(
|
||||
"character-animation",
|
||||
LocalProjectResourceEditKind::CharacterAnimation,
|
||||
),
|
||||
("image", LocalProjectResourceEditKind::ImageReference),
|
||||
] {
|
||||
let authority = resource_edit_prompt_max_chars(&edit_kind);
|
||||
let mode = if matches!(
|
||||
edit_kind,
|
||||
LocalProjectResourceEditKind::ImageReference
|
||||
| LocalProjectResourceEditKind::CharacterAnimation
|
||||
) {
|
||||
"derive"
|
||||
} else {
|
||||
"create"
|
||||
};
|
||||
let mut arguments = json!({
|
||||
"kind": kind,
|
||||
"mode": mode,
|
||||
"prompt": "字".repeat(authority),
|
||||
"assetName": "边界名称"
|
||||
});
|
||||
if mode == "derive" {
|
||||
arguments["sourceLocalAssetId"] = json!("registered-source");
|
||||
}
|
||||
bridge_resource_generation_input(&arguments)
|
||||
.unwrap_or_else(|error| panic!("{kind} 恰好等于上限必须通过:{error}"));
|
||||
|
||||
arguments["prompt"] = json!("字".repeat(authority + 1));
|
||||
let error = match bridge_resource_generation_input(&arguments) {
|
||||
Ok(_) => panic!("{kind} 超过按 kind 上限的提示词必须被拒绝"),
|
||||
Err(error) => error,
|
||||
};
|
||||
assert!(
|
||||
error.contains(&authority.to_string()) && error.contains(kind_label(&edit_kind)),
|
||||
"{kind} 的拒绝文案必须带上真实上限与类型:{error}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn kind_label(edit_kind: &LocalProjectResourceEditKind) -> &'static str {
|
||||
match edit_kind {
|
||||
LocalProjectResourceEditKind::BackgroundMusic => "背景音乐",
|
||||
LocalProjectResourceEditKind::SoundEffect => "音效",
|
||||
LocalProjectResourceEditKind::Video => "视频",
|
||||
LocalProjectResourceEditKind::CharacterAnimation => "角色动画",
|
||||
_ => "资源编辑",
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bridge_argument_bounds_are_deterministic() {
|
||||
assert_eq!(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -728,7 +728,16 @@ fn validate_resource_edit_uuid(value: &str, label: &str) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resource_edit_prompt_max_chars(edit_kind: &LocalProjectResourceEditKind) -> usize {
|
||||
/// 资源编辑提示词上限的**唯一口径**。
|
||||
///
|
||||
/// 三个调用方都必须从这里取数,禁止各自写死数字:
|
||||
/// 1. 本文件的提交校验(`normalize_resource_edit_prompt`);
|
||||
/// 2. `agc_tools` MCP 工具层(`direct_tools_mcp.rs` 的参数校验与工具 schema);
|
||||
/// 3. 客户端受控工具桥(`direct_tool_bridge.rs`)。
|
||||
///
|
||||
/// 客户端 UI 的 `resourceEditPromptMaxLength`(`resourceEditModel.ts`)是同一份口径的
|
||||
/// 前端镜像;改数字必须同时改这里、那里,以及工具 schema 里按 kind 声明 `maxLength`。
|
||||
pub(crate) fn resource_edit_prompt_max_chars(edit_kind: &LocalProjectResourceEditKind) -> usize {
|
||||
match edit_kind {
|
||||
LocalProjectResourceEditKind::BackgroundMusic => 140,
|
||||
LocalProjectResourceEditKind::SoundEffect => 1_900,
|
||||
@@ -739,6 +748,24 @@ fn resource_edit_prompt_max_chars(edit_kind: &LocalProjectResourceEditKind) -> u
|
||||
}
|
||||
}
|
||||
|
||||
/// 提示词超限的拒绝文案:与上限同一个口径,MCP 层、工具桥和提交校验复用同一条字符串,
|
||||
/// 保证模型看到的数字就是真实生效的数字。
|
||||
pub(crate) fn resource_edit_prompt_limit_error(
|
||||
edit_kind: &LocalProjectResourceEditKind,
|
||||
max_chars: usize,
|
||||
) -> String {
|
||||
format!(
|
||||
"{}资源编辑提示词必须在 1..={max_chars} 字符内",
|
||||
match edit_kind {
|
||||
LocalProjectResourceEditKind::BackgroundMusic => "背景音乐",
|
||||
LocalProjectResourceEditKind::SoundEffect => "音效",
|
||||
LocalProjectResourceEditKind::Video => "视频",
|
||||
LocalProjectResourceEditKind::CharacterAnimation => "角色动画",
|
||||
_ => "",
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn normalize_resource_edit_prompt(
|
||||
edit_kind: &LocalProjectResourceEditKind,
|
||||
value: &str,
|
||||
@@ -746,16 +773,7 @@ fn normalize_resource_edit_prompt(
|
||||
let value = value.trim();
|
||||
let max_chars = resource_edit_prompt_max_chars(edit_kind);
|
||||
if value.is_empty() || value.chars().count() > max_chars {
|
||||
return Err(format!(
|
||||
"{}资源编辑提示词必须在 1..={max_chars} 字符内",
|
||||
match edit_kind {
|
||||
LocalProjectResourceEditKind::BackgroundMusic => "背景音乐",
|
||||
LocalProjectResourceEditKind::SoundEffect => "音效",
|
||||
LocalProjectResourceEditKind::Video => "视频",
|
||||
LocalProjectResourceEditKind::CharacterAnimation => "角色动画",
|
||||
_ => "",
|
||||
}
|
||||
));
|
||||
return Err(resource_edit_prompt_limit_error(edit_kind, max_chars));
|
||||
}
|
||||
if value
|
||||
.chars()
|
||||
|
||||
@@ -224,6 +224,12 @@ export function defaultCharacterAnimationResourceName(
|
||||
return `${resourceBaseName(resource) || '资源'}-角色动画`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源编辑提示词上限:与 Rust `resource_edit_prompt_max_chars`
|
||||
* (`src-tauri/src/project/resource_editor.rs`)逐值同口径,UI、资源编辑提交、
|
||||
* `agc_tools` MCP 工具层与客户端工具桥共用同一组数字。改这里必须同时改那里,
|
||||
* 并按 kind 同步 `direct_tools_mcp.rs` 工具 schema 里的 `prompt.maxLength`。
|
||||
*/
|
||||
export function resourceEditPromptMaxLength(
|
||||
editKind: LocalProjectResourceEditKind,
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user