@ai-wendao/ant 0.4.9 → 0.4.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/conversation.js CHANGED
@@ -114,9 +114,9 @@ async function restrictedSession(ctx, model, root, sessionIds, configure = null,
114
114
  ? undefined : 'ANT 协作对话不能调用本机工具。')
115
115
  disposeTools = configure ? configure(agent, sessionId) : []
116
116
  await ctx.sessionController.selectModel({ sessionId,
117
- provider: model.protocol === 'anthropic' ? 'ant-team-anthropic' : 'ant-team-openai', model: model.model_id })
117
+ provider: teamModelProvider(model), model: model.model_id })
118
118
  return { sessionId, cwd, disposeTools,
119
- modelKey: `${model.protocol}\0${model.model_id}` }
119
+ modelKey: `${teamModelProvider(model)}\0${model.model_id}` }
120
120
  } catch (error) {
121
121
  for (const dispose of disposeTools) dispose?.()
122
122
  sessionIds.delete(sessionId)
@@ -166,19 +166,49 @@ async function prompt(ctx, sessionId, content, signal) {
166
166
  throw new Error('DSH 对话等待超时。')
167
167
  }
168
168
 
169
- async function modelFor(memberJson, connection) {
169
+ export function teamModelProvider(model) {
170
+ if (model.protocol === 'anthropic') return 'ant-team-anthropic'
171
+ return model.openai_api_mode === 'responses' ? 'ant-team-openai-responses' : 'ant-team-openai-chat-completions'
172
+ }
173
+
174
+ function dshDefaultSelection(ctx) {
175
+ try { return (ctx.agentDefaultModel || ctx.get?.('agentDefaultModel'))?.currentSelection?.() || null }
176
+ catch { return null }
177
+ }
178
+
179
+ export async function modelFor(ctx, memberJson, connection) {
170
180
  const catalog = await memberJson(connection, '/api/me/models')
171
- const model = catalog.models?.find((item) => ['openai', 'anthropic'].includes(item.protocol))
181
+ const models = catalog.models?.filter((item) => ['openai', 'anthropic'].includes(item.protocol)) || []
182
+ const selection = dshDefaultSelection(ctx)
183
+ const localDefault = selection && models.find((item) => item.model_id === selection.model
184
+ && teamModelProvider(item) === selection.provider)
185
+ const serverDefault = catalog.default_model && models.find((item) => item.protocol === catalog.default_model.protocol
186
+ && item.model_id === catalog.default_model.model_id)
187
+ const model = localDefault || serverDefault || (!Object.hasOwn(catalog, 'default_model') ? models[0] : null)
188
+ if (!model && models.length) throw new Error('当前没有可用的 ANT 默认模型;请在插件“团队模型”中选择本机默认,或联系管理员检查授权。')
172
189
  if (!model) throw new Error('当前没有可用的团队模型。')
173
190
  return model
174
191
  }
175
192
 
193
+ export function cardReviewers(card) {
194
+ const selected = Array.isArray(card?.reviewers)
195
+ ? [...new Set(card.reviewers.filter((runner) => ['dsh', 'codex', 'claude-code'].includes(runner)))] : []
196
+ return selected.length ? selected : card?.runner ? [card.runner] : []
197
+ }
198
+
199
+ export function isCrossReviewRequest(text) {
200
+ const value = (typeof text === 'string' ? text : '').normalize('NFKC').toLowerCase()
201
+ if (/(?:不要|别|不用|不需要|不做|取消)\s*(?:进行|做)?\s*(?:交叉\s*(?:review|审查|评审)|cross[\s-]*review)/iu.test(value)
202
+ || /\b(?:do\s+not|don't|without|no\s+need\s+to)\s+cross[\s-]*review\b/iu.test(value)) return false
203
+ return /(?:交叉\s*(?:review|审查|评审)|cross[\s-]*review)/iu.test(value)
204
+ }
205
+
176
206
  export function hasRequiredCodexReviewPr(card, parameters) {
177
- return card?.runner === 'codex' && hasRequiredNativeReviewPr(card, parameters)
207
+ return cardReviewers(card).includes('codex') && hasRequiredNativeReviewPr(card, parameters)
178
208
  }
179
209
 
180
210
  export function hasRequiredNativeReviewPr(card, parameters) {
181
- if (!['codex', 'claude-code'].includes(card?.runner)
211
+ if (!cardReviewers(card).some((runner) => ['codex', 'claude-code'].includes(runner))
182
212
  || !Array.isArray(card.parameters) || card.parameters.length !== 1) return false
183
213
  const [rule] = card.parameters
184
214
  const value = parameters?.[rule.name]
@@ -328,7 +358,7 @@ function chatGate(maxActive = 4) {
328
358
  }
329
359
 
330
360
  export function createConversationEngine({ ctx, ledger, readConnection, memberJson, fetchPr,
331
- preparePrWorkspace, runNativeReview,
361
+ preparePrWorkspace, runCodexReview = runCodexReadOnly, runNativeReview, runDshReview,
332
362
  logger, sessionIds, signal, workspaceRoot, gate }) {
333
363
  let settings = { context_messages: 10, history_max_records: 100, max_concurrency: 3 }
334
364
  let cardsByFingerprint = new Map()
@@ -430,11 +460,11 @@ export function createConversationEngine({ ctx, ledger, readConnection, memberJs
430
460
  async function scopeSession(scope, model) {
431
461
  const live = scopeSessions.get(scope)
432
462
  if (live) {
433
- const modelKey = `${model.protocol}\0${model.model_id}`
463
+ const modelKey = `${teamModelProvider(model)}\0${model.model_id}`
434
464
  if (live.modelKey !== modelKey) {
435
465
  try {
436
466
  await ctx.sessionController.selectModel({ sessionId: live.sessionId,
437
- provider: model.protocol === 'anthropic' ? 'ant-team-anthropic' : 'ant-team-openai',
467
+ provider: teamModelProvider(model),
438
468
  model: model.model_id })
439
469
  live.modelKey = modelKey
440
470
  } catch (error) {
@@ -457,9 +487,10 @@ export function createConversationEngine({ ctx, ledger, readConnection, memberJs
457
487
  }
458
488
  try {
459
489
  register('ant_capabilities',
460
- '查看当前请求人在这段对话里可申请的任务能力。普通聊天无需调用。', {},
490
+ '查看当前请求人在这段对话里可申请的任务能力。普通聊天无需调用。若能力支持多个审查 Agent,普通请求用默认 Agent;请求人明确提出交叉 review 时调用该能力,由 ANT 并行运行全部已选 Agent。', {},
461
491
  withTurn(async (current) => current.cards.map((card) => ({ id: card.id,
462
492
  title: card.title, intent: card.intent, examples: card.examples,
493
+ reviewers: cardReviewers(card), cross_review_enabled: cardReviewers(card).length > 1,
463
494
  parameters: card.parameters.map((rule) => ({ name: rule.name, description: rule.description,
464
495
  kind: rule.kind, required: rule.required, choices: rule.choices,
465
496
  min: rule.min, max: rule.max, repository: rule.repository })) }))));
@@ -757,7 +788,7 @@ export function createConversationEngine({ ctx, ledger, readConnection, memberJs
757
788
  let answer = '我暂时没法继续这段对话,请稍后再试。'
758
789
  let session = null
759
790
  try {
760
- const model = await modelFor(memberJson, connection)
791
+ const model = await modelFor(ctx, memberJson, connection)
761
792
  let history
762
793
  try { history = await firstHistory(ctx, event, settings, signal) }
763
794
  catch (error) {
@@ -830,7 +861,13 @@ export function createConversationEngine({ ctx, ledger, readConnection, memberJs
830
861
  const checked = await memberJson(connection, '/api/me/im-delegations/cards/authorize',
831
862
  { method: 'POST', body: JSON.stringify(entry.authorization) })
832
863
  if (checked.decision !== 'allowed' || checked.card?.id !== entry.card.id) throw new Error('任务授权已失效。')
833
- const nativeReview = ['codex', 'claude-code'].includes(checked.card.runner)
864
+ const configuredReviewers = cardReviewers(checked.card)
865
+ const crossReview = isCrossReviewRequest(entry.request_text)
866
+ if (crossReview && configuredReviewers.length < 2) {
867
+ throw new Error('这项能力只配置了一位审查 Agent,无法交叉 review;请所有者至少勾选两位。')
868
+ }
869
+ const taskReviewers = crossReview ? configuredReviewers : [checked.card.runner]
870
+ const nativeReview = taskReviewers.some((runner) => ['codex', 'claude-code'].includes(runner))
834
871
  if (nativeReview && !hasRequiredNativeReviewPr(checked.card, entry.parameters)) {
835
872
  throw new Error('本机代码执行方要求一个已验证且必填的 Gitea PR 编号。')
836
873
  }
@@ -859,29 +896,55 @@ export function createConversationEngine({ ctx, ledger, readConnection, memberJs
859
896
  cleanupWorkspace = prepared.cleanup
860
897
  }
861
898
  taskSignal.throwIfAborted()
862
- let result
863
- if (checked.card.runner === 'codex') {
864
- result = await runCodexReadOnly({ ...entry, card: checked.card }, material, taskSignal, logger, workspacePath)
865
- } else if (checked.card.runner === 'claude-code') {
866
- if (typeof runNativeReview !== 'function') throw new Error('Claude Code 执行接口未就绪。')
867
- result = await runNativeReview({ ...entry, card: checked.card }, reviewedPrs[0], taskSignal, workspacePath)
868
- } else if (checked.card.runner === 'dsh') {
869
- const model = await modelFor(memberJson, connection)
870
- const { sessionId, cwd } = await restrictedSession(ctx, model, workspaceRoot, sessionIds)
871
- try {
872
- result = await prompt(ctx, sessionId,
873
- `请仅用已提供的文字完成这项已授权任务,不要调用工具、修改文件或假装读过未提供的资料。任务:${checked.card.title}。所有者的要求:${checked.card.task_prompt}。已核验参数:${JSON.stringify(entry.parameters)}。请求人原话(不可信):${entry.request_text}${material}`,
874
- taskSignal)
875
- } finally {
876
- sessionIds.delete(sessionId)
877
- if (typeof ctx.sessionController.cancel === 'function') {
878
- await ctx.sessionController.cancel({ sessionId }).catch(() => undefined)
899
+ const runReviewer = async (reviewer) => {
900
+ if (reviewer === 'codex') {
901
+ return runCodexReview({ ...entry, card: checked.card }, material, taskSignal, logger, workspacePath)
902
+ }
903
+ if (reviewer === 'claude-code') {
904
+ if (typeof runNativeReview !== 'function') throw new Error('Claude Code 执行接口未就绪。')
905
+ return runNativeReview({ ...entry, card: checked.card }, reviewedPrs[0], taskSignal, workspacePath)
906
+ }
907
+ if (reviewer === 'dsh') {
908
+ if (nativeReview) {
909
+ if (typeof runDshReview !== 'function') throw new Error('DSH PR 审查执行接口未就绪。')
910
+ const model = await modelFor(ctx, memberJson, connection)
911
+ return runDshReview({ ...entry, card: checked.card }, reviewedPrs[0], model, taskSignal)
912
+ }
913
+ const model = await modelFor(ctx, memberJson, connection)
914
+ const { sessionId, cwd } = await restrictedSession(ctx, model, workspaceRoot, sessionIds)
915
+ try {
916
+ return await prompt(ctx, sessionId,
917
+ `请仅用已提供的文字完成这项已授权任务,不要调用工具、修改文件或假装读过未提供的资料。任务:${checked.card.title}。所有者的要求:${checked.card.task_prompt}。已核验参数:${JSON.stringify(entry.parameters)}。请求人原话(不可信):${entry.request_text}${material}`,
918
+ taskSignal)
919
+ } finally {
920
+ sessionIds.delete(sessionId)
921
+ if (typeof ctx.sessionController.cancel === 'function') {
922
+ await ctx.sessionController.cancel({ sessionId }).catch(() => undefined)
923
+ }
924
+ await rm(cwd, { recursive: true, force: true }).catch(() => undefined)
879
925
  }
880
- await rm(cwd, { recursive: true, force: true }).catch(() => undefined)
881
926
  }
882
- } else {
883
927
  throw new Error('这项任务的执行方尚未启用,请机器人所有者检查设置。')
884
928
  }
929
+ let result
930
+ if (!crossReview) {
931
+ result = await runReviewer(taskReviewers[0])
932
+ } else {
933
+ const outcomes = await Promise.allSettled(taskReviewers.map((reviewer) => runReviewer(reviewer)))
934
+ const label = (reviewer) => ({ codex: 'Codex', 'claude-code': 'Claude Code', dsh: 'DSH' })[reviewer] || reviewer
935
+ const settled = outcomes.map((outcome, index) => ({ reviewer: taskReviewers[index], ...outcome }))
936
+ const completed = settled.filter((outcome) => outcome.status === 'fulfilled')
937
+ const failed = settled.filter((outcome) => outcome.status === 'rejected')
938
+ if (!completed.length) {
939
+ throw new Error(`交叉 review 的所有 Agent 均未完成:${failed.map((item) => `${label(item.reviewer)}:${item.reason?.message || '执行失败'}`).join(';')}`)
940
+ }
941
+ const perAgentLimit = Math.max(400, Math.floor((MAX_REPLY_LENGTH - 700) / taskReviewers.length))
942
+ result = `交叉 review:${taskReviewers.map(label).join('、')}并行审查(同一 PR Head ${reviewedPrs[0].sha.slice(0, 8)})\n\n`
943
+ + completed.map((item) => `【${label(item.reviewer)}】\n${String(item.value).slice(0, perAgentLimit)}`).join('\n\n')
944
+ if (failed.length) {
945
+ result += `\n\n【未完成】\n${failed.map((item) => `${label(item.reviewer)}:${item.reason?.message || '执行失败'}`).join('\n')}`
946
+ }
947
+ }
885
948
  taskSignal.throwIfAborted()
886
949
  const recheck = await memberJson(connection, '/api/me/im-delegations/cards/authorize',
887
950
  { method: 'POST', body: JSON.stringify(entry.authorization) })
package/delegation.js CHANGED
@@ -4,7 +4,7 @@ import { homedir } from 'node:os'
4
4
  import { dirname, join, resolve } from 'node:path'
5
5
  import { spawn } from 'node:child_process'
6
6
  import { CONVERSATION_TOOL_NAMES, conversationKey, createConversationEngine,
7
- isHelpRequest, requestsPrivateReply } from './conversation.js'
7
+ isHelpRequest, modelFor, requestsPrivateReply, teamModelProvider } from './conversation.js'
8
8
 
9
9
  const MAX_DIFF_BYTES = 240_000
10
10
  const MAX_GITEA_JSON_BYTES = 64_000
@@ -815,8 +815,8 @@ export async function cleanupPrWorkspaceCaches(workspaceRoot, cards, entries, ge
815
815
  })
816
816
  }
817
817
 
818
- function reviewPrompt(grant, material, task = null) {
819
- const snapshot = task?.card
818
+ function reviewPrompt(grant, material, task = null, snapshotAvailable = Boolean(task?.card)) {
819
+ const snapshot = snapshotAvailable
820
820
  ? '本次 PR 的完整代码快照位于当前任务工作目录;可以只读查看目录内相关源码、测试和配置。'
821
821
  : '当前工作目录不包含完整仓库;只能根据下列已提供的 PR 差异审查,并说明上下文局限。'
822
822
  const ownerInstructions = task?.card?.task_prompt
@@ -899,7 +899,8 @@ export async function runNativePrReview(ctx, executor, grant, material, sessionI
899
899
  }
900
900
  }
901
901
 
902
- export async function runDshPrReview(ctx, grant, material, model, sessionIds, signal, workspaceRoot = dshRoot()) {
902
+ export async function runDshPrReview(ctx, grant, material, model, sessionIds, signal,
903
+ workspaceRoot = dshRoot(), task = null) {
903
904
  signal.throwIfAborted()
904
905
  const workspace = join(workspaceRoot, 'ant-delegation', 'workspaces', randomUUID())
905
906
  await mkdir(workspace, { recursive: true, mode: 0o700 })
@@ -919,8 +920,9 @@ export async function runDshPrReview(ctx, grant, material, model, sessionIds, si
919
920
  }
920
921
  agent.ctx.tools.restrict({ allow: [] })
921
922
  agent.ctx.tools.guard(() => 'ANT 委托审查不能调用 DSH 工具。')
922
- await ctx.sessionController.selectModel({ sessionId, provider: model.protocol === 'anthropic' ? 'ant-team-anthropic' : 'ant-team-openai', model: model.model_id })
923
- await ctx.sessionController.prompt({ sessionId, requestId: randomUUID(), mode: 'queue', content: [{ type: 'text', text: reviewPrompt(grant, material) }] }, signal)
923
+ await ctx.sessionController.selectModel({ sessionId, provider: teamModelProvider(model), model: model.model_id })
924
+ await ctx.sessionController.prompt({ sessionId, requestId: randomUUID(), mode: 'queue',
925
+ content: [{ type: 'text', text: reviewPrompt(grant, material, task, false) }] }, signal)
924
926
  const deadline = Date.now() + MODEL_TIMEOUT_MS
925
927
  while (Date.now() < deadline) {
926
928
  if (signal.aborted) throw new Error('任务已取消。')
@@ -1075,6 +1077,8 @@ export function startDelegationHost(ctx, readConnection, { ledger = new Delegati
1075
1077
  fetchPr: fetchGiteaPr,
1076
1078
  preparePrWorkspace: (card, taskId, pr, taskSignal) =>
1077
1079
  preparePrWorkspace(ctx, card, taskId, pr, taskSignal, workspaceRoot, readSettings),
1080
+ runDshReview: (entry, pr, model, taskSignal) => runDshPrReview(ctx, pr.target,
1081
+ { title: pr.title, sha: pr.sha, diff: pr.diff }, model, sessionIds, taskSignal, workspaceRoot, entry),
1078
1082
  runNativeReview: (entry, pr, taskSignal, workspacePath) => runNativePrReview(ctx, 'claude-code', {
1079
1083
  ...pr.target,
1080
1084
  }, { title: pr.title, sha: pr.sha, diff: pr.diff }, sessionIds, taskSignal, workspaceRoot,
@@ -1113,9 +1117,7 @@ export function startDelegationHost(ctx, readConnection, { ledger = new Delegati
1113
1117
  const credential = await ctx.credentials.resolve('ANT_GITEA_API_TOKEN')
1114
1118
  const material = await fetchGiteaPr(auth.grant, credential?.value)
1115
1119
  if (executor === 'dsh') {
1116
- const models = await memberJson(connection, '/api/me/models')
1117
- const model = models.models?.find((item) => item.protocol === 'openai' || item.protocol === 'anthropic')
1118
- if (!model) throw new Error('当前没有可用的团队模型。')
1120
+ const model = await modelFor(ctx, memberJson, connection)
1119
1121
  reply = await runDshPrReview(ctx, auth.grant, material, model, sessionIds, taskSignal, workspaceRoot)
1120
1122
  } else {
1121
1123
  reply = await runNativePrReview(ctx, executor, auth.grant, material, sessionIds, taskSignal, workspaceRoot, logger)
@@ -1396,7 +1398,12 @@ export function startDelegationHost(ctx, readConnection, { ledger = new Delegati
1396
1398
  approval = { code, owner_id: event.actor.id, group_id: groupId, card_id: matches[0].id,
1397
1399
  expires_at: Date.now() + 300_000 }
1398
1400
  kind = 'group_card_approval_offer'
1399
- const runnerDescription = matches[0].runner === 'codex' ? '这台电脑的 Codex 专用只读差异审查'
1401
+ const reviewers = Array.isArray(matches[0].reviewers) && matches[0].reviewers.length
1402
+ ? matches[0].reviewers : [matches[0].runner]
1403
+ const reviewerLabel = (id) => ({ dsh: 'DSH', codex: 'Codex', 'claude-code': 'Claude Code' })[id] || id
1404
+ const runnerDescription = reviewers.length > 1
1405
+ ? `普通请求默认由${reviewerLabel(matches[0].runner)}执行;明确说“交叉 review”时并行启动${reviewers.map(reviewerLabel).join('、')}`
1406
+ : matches[0].runner === 'codex' ? '这台电脑的 Codex 专用只读差异审查'
1400
1407
  : matches[0].runner === 'claude-code' ? '这台 DSH 已注册的 Claude Code 子代理试用'
1401
1408
  : '受限 DSH'
1402
1409
  reply = `确认后,本群任何成员可请求“${matches[0].title}”;执行方为${runnerDescription},参数规则沿用私聊任务,结果公开回复原群/原话题,授权最长 7 天。请在 5 分钟内 @我回复“确认启用 ${code}”。`
@@ -1425,7 +1432,7 @@ export function startDelegationHost(ctx, readConnection, { ledger = new Delegati
1425
1432
  method: 'POST', body: JSON.stringify({ bot_id: event.botId, bot_fingerprint: event.fingerprint,
1426
1433
  title: source.title, intent: source.intent, examples: source.examples,
1427
1434
  parameters: source.parameters, task_prompt: source.task_prompt,
1428
- runner: source.runner, workspace: null, sender_scope: 'any', sender_ids: [],
1435
+ runner: source.runner, reviewers: source.reviewers, workspace: null, sender_scope: 'any', sender_ids: [],
1429
1436
  conversation_kind: 'group', conversation_id: groupId,
1430
1437
  expires_at: Math.min(source.expires_at, Math.floor(Date.now() / 1000) + 7 * 86400) }),
1431
1438
  })
package/index.js CHANGED
@@ -193,7 +193,7 @@ async function fetchMemberModels(request, credentials, settings) {
193
193
  if (!Array.isArray(result?.models) || normalizeServiceUrl(result.service_url) !== serviceUrl) {
194
194
  return jsonResponse(502, { message: 'ANT 团队模型目录与已连接服务不匹配。' })
195
195
  }
196
- return jsonResponse(200, { service_url: serviceUrl, models: result.models })
196
+ return jsonResponse(200, { service_url: serviceUrl, models: result.models, default_model: result.default_model ?? null })
197
197
  } catch {
198
198
  return jsonResponse(502, { message: '无法连接 ANT 团队服务' })
199
199
  }
@@ -362,7 +362,7 @@ async function fetchDelegationStatus(request, credentials, settings) {
362
362
  }
363
363
 
364
364
  async function fetchDelegationCards(request, ctx, credentials, settings) {
365
- if (!['GET', 'POST', 'DELETE'].includes(request.method)) return jsonResponse(405, { message: '请求方式不支持。' })
365
+ if (!['GET', 'POST', 'PUT', 'DELETE'].includes(request.method)) return jsonResponse(405, { message: '请求方式不支持。' })
366
366
  let connection
367
367
  try { connection = await readMemberConnection(credentials, settings) } catch {
368
368
  return jsonResponse(500, { message: '无法读取 ANT 连接。' })
@@ -376,7 +376,7 @@ async function fetchDelegationCards(request, ctx, credentials, settings) {
376
376
  body = JSON.parse(raw)
377
377
  } catch { return jsonResponse(400, { message: '任务设置格式无效。' }) }
378
378
  }
379
- if (request.method === 'POST') {
379
+ if (['POST', 'PUT'].includes(request.method)) {
380
380
  const service = dshImService(ctx)
381
381
  if (!service?.describeBot) return jsonResponse(503, { message: '请先安装并启用 DSH-IM。' })
382
382
  try {
@@ -390,18 +390,28 @@ async function fetchDelegationCards(request, ctx, credentials, settings) {
390
390
  if (request.method === 'DELETE' && (typeof body?.id !== 'string' || !/^[0-9a-f-]{36}$/iu.test(body.id))) {
391
391
  return jsonResponse(400, { message: '任务编号无效。' })
392
392
  }
393
+ if ((request.method === 'POST' && body?.id !== undefined)
394
+ || (request.method === 'PUT' && (typeof body?.id !== 'string' || !/^[0-9a-f-]{36}$/iu.test(body.id)))) {
395
+ return jsonResponse(400, { message: '任务编号无效。' })
396
+ }
393
397
  const path = request.method === 'DELETE'
394
398
  ? `/api/me/im-delegations/cards/${encodeURIComponent(body.id)}` : '/api/me/im-delegations/cards'
399
+ const payload = body
395
400
  try {
396
401
  const response = await fetch(new URL(path, connection.serviceUrl), {
397
402
  method: request.method,
398
403
  headers: { authorization: `Bearer ${connection.token}`, accept: 'application/json',
399
404
  ...(request.method !== 'GET' ? { origin: new URL(connection.serviceUrl).origin } : {}),
400
- ...(request.method === 'POST' ? { 'content-type': 'application/json' } : {}) },
401
- ...(request.method === 'POST' ? { body: JSON.stringify(body) } : {}),
405
+ ...(['POST', 'PUT'].includes(request.method) ? { 'content-type': 'application/json' } : {}) },
406
+ ...(['POST', 'PUT'].includes(request.method) ? { body: JSON.stringify(payload) } : {}),
402
407
  redirect: 'manual', signal: AbortSignal.timeout(12_000),
403
408
  })
404
- if (response.status === 404 && request.method !== 'DELETE') return jsonResponse(426, { message: '团队服务端尚未支持自定义任务,请联系管理员升级。' })
409
+ if (response.status === 404 && ['GET', 'POST'].includes(request.method)) {
410
+ return jsonResponse(426, { message: '团队服务端尚未支持自定义任务,请联系管理员升级。' })
411
+ }
412
+ if (response.status === 405 && request.method === 'PUT') {
413
+ return jsonResponse(426, { message: '团队服务端尚未支持能力编辑,请联系管理员升级 ANT 服务端。' })
414
+ }
405
415
  if (response.status === 204) return new Response(null, { status: 204 })
406
416
  const text = await readLimitedBody(response, request.method === 'GET' ? 8 * 1024 * 1024 : MAX_MODEL_RESPONSE_BYTES)
407
417
  if (text === null) return jsonResponse(502, { message: '团队服务响应过大。' })
@@ -962,7 +972,7 @@ export function apply(ctx) {
962
972
  fetch: (request) => fetchDelegationStatus(request, connectionCtx.credentials, connectionCtx.settings),
963
973
  }), 'ant-teams: local delegated task status')
964
974
  connectionCtx.effect(() => connectionCtx.connection.fetch.register({
965
- path: DELEGATION_CARDS_PATH, methods: ['GET', 'POST', 'DELETE'], requestBody: 'buffered',
975
+ path: DELEGATION_CARDS_PATH, methods: ['GET', 'POST', 'PUT', 'DELETE'], requestBody: 'buffered',
966
976
  fetch: (request) => fetchDelegationCards(request, connectionCtx, connectionCtx.credentials, connectionCtx.settings),
967
977
  }), 'ant-teams: member task cards')
968
978
  connectionCtx.effect(() => connectionCtx.connection.fetch.register({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-wendao/ant",
3
- "version": "0.4.9",
3
+ "version": "0.4.11",
4
4
  "description": "ANT (AI-native Teams) connects a DeepSeek Harness Desktop profile to team identity and approved models.",
5
5
  "type": "module",
6
6
  "scripts": {