@ai-wendao/ant 0.4.0 → 0.4.1

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/index.js CHANGED
@@ -3,7 +3,8 @@ export const name = 'ant'
3
3
  import { createHash } from 'node:crypto'
4
4
  import { createRequire } from 'node:module'
5
5
  import { installSkill, listSkills, uninstallSkill } from './skill-install.js'
6
- import { recentDelegationStatus, startDelegationHost } from './delegation.js'
6
+ import { readLocalDelegationSettings, recentDelegationStatus, startDelegationHost,
7
+ writeLocalDelegationSettings } from './delegation.js'
7
8
 
8
9
  export const inject = []
9
10
 
@@ -25,6 +26,11 @@ const DELEGATION_ENROLLED_BOTS_PATH = '/api/ant-teams/delegation/enrolled-bots'
25
26
  const DELEGATION_GROUPS_PATH = '/api/ant-teams/delegation/groups'
26
27
  const DELEGATION_OWNER_CHALLENGE_PATH = '/api/ant-teams/delegation/owner-challenge'
27
28
  const DELEGATION_STATUS_PATH = '/api/ant-teams/delegation/status'
29
+ const DELEGATION_CARDS_PATH = '/api/ant-teams/delegation/cards'
30
+ const DELEGATION_SETTINGS_PATH = '/api/ant-teams/delegation/settings'
31
+ const DELEGATION_EXECUTORS_PATH = '/api/ant-teams/delegation/executors'
32
+ const DELEGATION_LOCAL_SETTINGS_PATH = '/api/ant-teams/delegation/local-settings'
33
+ const DELEGATION_CANCEL_PATH = '/api/ant-teams/delegation/cancel'
28
34
  const ANT_NPM_PACKAGE_NAME = '@ai-wendao/ant'
29
35
  const BUNDLE_VERSION = createRequire(import.meta.url)('./package.json').version
30
36
  const MAX_MODEL_RESPONSE_BYTES = 1024 * 1024
@@ -124,7 +130,7 @@ async function updateModelSession(serviceUrl, token, action) {
124
130
  })
125
131
  }
126
132
 
127
- async function readLimitedBody(response) {
133
+ async function readLimitedBody(response, maxBytes = MAX_MODEL_RESPONSE_BYTES) {
128
134
  if (!response.body) return ''
129
135
  const reader = response.body.getReader()
130
136
  const chunks = []
@@ -134,7 +140,7 @@ async function readLimitedBody(response) {
134
140
  const { done, value } = await reader.read()
135
141
  if (done) break
136
142
  size += value.byteLength
137
- if (size > MAX_MODEL_RESPONSE_BYTES) {
143
+ if (size > maxBytes) {
138
144
  await reader.cancel()
139
145
  return null
140
146
  }
@@ -210,6 +216,7 @@ async function fetchDelegationBots(request, ctx) {
210
216
  connected: details.connected === true,
211
217
  available: details.capabilities?.includes('exclusive-text-consumer')
212
218
  && details.capabilities?.includes('reply-text-checked'),
219
+ history_available: details.capabilities?.includes('history-text-checked') === true,
213
220
  }
214
221
  } catch { return null }
215
222
  }))
@@ -331,6 +338,111 @@ async function fetchDelegationStatus(request, credentials, settings) {
331
338
  } catch { return jsonResponse(503, { message: '本机委托任务记录暂时无法读取,请联系管理员检查。' }) }
332
339
  }
333
340
 
341
+ async function fetchDelegationCards(request, ctx, credentials, settings) {
342
+ if (!['GET', 'POST', 'DELETE'].includes(request.method)) return jsonResponse(405, { message: '请求方式不支持。' })
343
+ let connection
344
+ try { connection = await readMemberConnection(credentials, settings) } catch {
345
+ return jsonResponse(500, { message: '无法读取 ANT 连接。' })
346
+ }
347
+ if (!connection) return jsonResponse(401, { message: '请先连接 ANT 团队。' })
348
+ let body = null
349
+ if (request.method !== 'GET') {
350
+ try {
351
+ const raw = await request.text()
352
+ if (raw.length > 32_768) return jsonResponse(413, { message: '任务设置过大。' })
353
+ body = JSON.parse(raw)
354
+ } catch { return jsonResponse(400, { message: '任务设置格式无效。' }) }
355
+ }
356
+ if (request.method === 'POST') {
357
+ const service = dshImService(ctx)
358
+ if (!service?.describeBot) return jsonResponse(503, { message: '请先安装并启用 DSH-IM。' })
359
+ try {
360
+ const bot = await service.describeBot(body.bot_id)
361
+ if (bot.channel !== 'feishu' || !bot.connected || bot.account?.fingerprint !== body.bot_fingerprint
362
+ || !bot.capabilities?.includes('exclusive-text-consumer') || !bot.capabilities?.includes('reply-text-checked')) {
363
+ return jsonResponse(400, { message: '请选择已连接且支持协作收件的飞书机器人。' })
364
+ }
365
+ } catch { return jsonResponse(400, { message: '机器人当前不可用。' }) }
366
+ }
367
+ if (request.method === 'DELETE' && (typeof body?.id !== 'string' || !/^[0-9a-f-]{36}$/iu.test(body.id))) {
368
+ return jsonResponse(400, { message: '任务编号无效。' })
369
+ }
370
+ const path = request.method === 'DELETE'
371
+ ? `/api/me/im-delegations/cards/${encodeURIComponent(body.id)}` : '/api/me/im-delegations/cards'
372
+ try {
373
+ const response = await fetch(new URL(path, connection.serviceUrl), {
374
+ method: request.method,
375
+ headers: { authorization: `Bearer ${connection.token}`, accept: 'application/json',
376
+ ...(request.method === 'POST' ? { 'content-type': 'application/json' } : {}) },
377
+ ...(request.method === 'POST' ? { body: JSON.stringify(body) } : {}),
378
+ redirect: 'manual', signal: AbortSignal.timeout(12_000),
379
+ })
380
+ if (response.status === 404 && request.method !== 'DELETE') return jsonResponse(426, { message: '团队服务端尚未支持自定义任务,请联系管理员升级。' })
381
+ if (response.status === 204) return new Response(null, { status: 204 })
382
+ const text = await readLimitedBody(response, request.method === 'GET' ? 8 * 1024 * 1024 : MAX_MODEL_RESPONSE_BYTES)
383
+ if (text === null) return jsonResponse(502, { message: '团队服务响应过大。' })
384
+ return new Response(text, { status: response.status,
385
+ headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' } })
386
+ } catch { return jsonResponse(502, { message: '暂时无法连接 ANT 团队服务。' }) }
387
+ }
388
+
389
+ async function fetchDelegationRuntimeSettings(request, credentials, settings) {
390
+ if (request.method !== 'GET') return jsonResponse(405, { message: '请求方式不支持。' })
391
+ try {
392
+ const connection = await readMemberConnection(credentials, settings)
393
+ if (!connection) return jsonResponse(401, { message: '请先连接 ANT 团队。' })
394
+ const response = await fetch(new URL('/api/me/im-delegations/settings', connection.serviceUrl), {
395
+ headers: { authorization: `Bearer ${connection.token}`, accept: 'application/json' },
396
+ redirect: 'manual', signal: AbortSignal.timeout(12_000),
397
+ })
398
+ if (!response.ok) return jsonResponse(response.status, { message: '暂时无法读取团队协作设置。' })
399
+ return jsonResponse(200, await response.json())
400
+ } catch { return jsonResponse(502, { message: '暂时无法读取团队协作设置。' }) }
401
+ }
402
+
403
+ function fetchDelegationExecutors(request, ctx) {
404
+ if (request.method !== 'GET') return jsonResponse(405, { message: '请求方式不支持。' })
405
+ let providers = []
406
+ try { providers = (ctx.get?.('subagents') ?? ctx.subagents)?.list?.() || [] } catch {}
407
+ const available = new Set(providers)
408
+ return jsonResponse(200, [
409
+ { id: 'dsh', title: 'DSH', available: Boolean(ctx.sessionController && ctx.agents) },
410
+ { id: 'codex', title: 'Codex', available: available.has('codex') },
411
+ { id: 'claude-code', title: 'Claude Code', available: available.has('claude-code') },
412
+ { id: 'acp', title: 'DSH ACP', available: available.has('acp') },
413
+ { id: 'dsh-sdk', title: 'DSH SDK 子代理', available: available.has('dsh-sdk') },
414
+ ])
415
+ }
416
+
417
+ async function fetchDelegationLocalSettings(request, credentials, settings) {
418
+ if (!['GET', 'PUT'].includes(request.method)) return jsonResponse(405, { message: '请求方式不支持。' })
419
+ try {
420
+ if (!await readMemberConnection(credentials, settings)) return jsonResponse(401, { message: '请先连接 ANT 团队。' })
421
+ if (request.method === 'GET') return jsonResponse(200, await readLocalDelegationSettings())
422
+ const raw = await request.text()
423
+ if (raw.length > 256) return jsonResponse(413, { message: '设置内容过大。' })
424
+ const body = JSON.parse(raw)
425
+ return jsonResponse(200, await writeLocalDelegationSettings(body))
426
+ } catch (error) { return jsonResponse(400, { message: error?.message || '本机设置无法保存。' }) }
427
+ }
428
+
429
+ async function fetchDelegationCancel(request, credentials, settings, getHost) {
430
+ if (request.method !== 'POST') return jsonResponse(405, { message: '请求方式不支持。' })
431
+ try {
432
+ const connection = await readMemberConnection(credentials, settings)
433
+ if (!connection) return jsonResponse(401, { message: '请先连接 ANT 团队。' })
434
+ const raw = await request.text()
435
+ if (raw.length > 256) return jsonResponse(413, { message: '请求内容过大。' })
436
+ const body = JSON.parse(raw)
437
+ if (typeof body.task_id !== 'string' || !/^[A-F0-9]{10}$/u.test(body.task_id)) {
438
+ return jsonResponse(400, { message: '任务编号无效。' })
439
+ }
440
+ const host = getHost()
441
+ if (!host) return jsonResponse(503, { message: '协作机器人尚未就绪。' })
442
+ return jsonResponse(200, { canceled: await host.cancelTask(body.task_id, connection.serviceUrl) })
443
+ } catch { return jsonResponse(503, { message: '暂时无法取消任务,请稍后重试。' }) }
444
+ }
445
+
334
446
  async function fetchServiceInfo(request, credentials, settings) {
335
447
  if (request.method !== 'POST') return jsonResponse(405, { message: '只支持 POST 请求' })
336
448
  let body
@@ -735,6 +847,7 @@ async function fetchAuthRequest(request, endpoint, credentials, settings) {
735
847
 
736
848
  export function apply(ctx) {
737
849
  ctx.inject(['connection', 'credentials', 'settings'], (connectionCtx) => {
850
+ let delegationHost = null
738
851
  connectionCtx.effect(() => connectionCtx.connection.fetch.register({
739
852
  path: SERVICE_INFO_PATH,
740
853
  methods: ['POST'],
@@ -803,6 +916,27 @@ export function apply(ctx) {
803
916
  path: DELEGATION_STATUS_PATH, methods: ['GET'], requestBody: 'buffered',
804
917
  fetch: (request) => fetchDelegationStatus(request, connectionCtx.credentials, connectionCtx.settings),
805
918
  }), 'ant-teams: local delegated task status')
919
+ connectionCtx.effect(() => connectionCtx.connection.fetch.register({
920
+ path: DELEGATION_CARDS_PATH, methods: ['GET', 'POST', 'DELETE'], requestBody: 'buffered',
921
+ fetch: (request) => fetchDelegationCards(request, connectionCtx, connectionCtx.credentials, connectionCtx.settings),
922
+ }), 'ant-teams: member task cards')
923
+ connectionCtx.effect(() => connectionCtx.connection.fetch.register({
924
+ path: DELEGATION_SETTINGS_PATH, methods: ['GET'], requestBody: 'buffered',
925
+ fetch: (request) => fetchDelegationRuntimeSettings(request, connectionCtx.credentials, connectionCtx.settings),
926
+ }), 'ant-teams: collaboration defaults')
927
+ connectionCtx.effect(() => connectionCtx.connection.fetch.register({
928
+ path: DELEGATION_EXECUTORS_PATH, methods: ['GET'], requestBody: 'buffered',
929
+ fetch: (request) => fetchDelegationExecutors(request, connectionCtx),
930
+ }), 'ant-teams: local executor availability')
931
+ connectionCtx.effect(() => connectionCtx.connection.fetch.register({
932
+ path: DELEGATION_LOCAL_SETTINGS_PATH, methods: ['GET', 'PUT'], requestBody: 'buffered',
933
+ fetch: (request) => fetchDelegationLocalSettings(request, connectionCtx.credentials, connectionCtx.settings),
934
+ }), 'ant-teams: local delegated-task concurrency')
935
+ connectionCtx.effect(() => connectionCtx.connection.fetch.register({
936
+ path: DELEGATION_CANCEL_PATH, methods: ['POST'], requestBody: 'buffered',
937
+ fetch: (request) => fetchDelegationCancel(request, connectionCtx.credentials,
938
+ connectionCtx.settings, () => delegationHost),
939
+ }), 'ant-teams: owner cancellation for delegated tasks')
806
940
  connectionCtx.effect(() => connectionCtx.connection.fetch.register({
807
941
  path: DISCONNECT_PATH,
808
942
  methods: ['POST'],
@@ -848,8 +982,15 @@ export function apply(ctx) {
848
982
  }
849
983
  }, 'ant-teams: ANT model activity session')
850
984
  connectionCtx.inject?.(['dshIm', 'sessionController', 'sessions', 'agents', 'tools'], (delegationCtx) => {
851
- delegationCtx.effect(() => startDelegationHost(delegationCtx,
852
- () => readMemberConnection(delegationCtx.credentials, delegationCtx.settings)),
985
+ delegationCtx.effect(() => {
986
+ const host = startDelegationHost(delegationCtx,
987
+ () => readMemberConnection(delegationCtx.credentials, delegationCtx.settings))
988
+ delegationHost = host
989
+ return async () => {
990
+ if (delegationHost === host) delegationHost = null
991
+ await host()
992
+ }
993
+ },
853
994
  'ant-teams: delegated IM review intake')
854
995
  })
855
996
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-wendao/ant",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
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": {
@@ -16,7 +16,7 @@
16
16
  "./client": "./client.js",
17
17
  "./locale/*.json": "./locale/*.json"
18
18
  },
19
- "files": ["index.js", "client.js", "skill-install.js", "delegation.js", "cordis.patch.yml", "locale/*.json", "tools/migrate-legacy.mjs", "MIGRATION.md", "README.md"],
19
+ "files": ["index.js", "client.js", "skill-install.js", "delegation.js", "conversation.js", "cordis.patch.yml", "locale/*.json", "tools/migrate-legacy.mjs", "MIGRATION.md", "README.md"],
20
20
  "dsh": {
21
21
  "bundle": {
22
22
  "patch": "./cordis.patch.yml"