@auto-ai/agent 2.1.102 → 2.1.104

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/ws-test.html CHANGED
@@ -2832,6 +2832,26 @@
2832
2832
  </div>
2833
2833
  </div>
2834
2834
 
2835
+ <div id="mcpItemConfigModal" class="agent-md-modal" aria-hidden="true">
2836
+ <div class="agent-md-modal-backdrop" id="mcpItemConfigModalBackdrop"></div>
2837
+ <div class="agent-md-modal-card" role="dialog" aria-modal="true" aria-labelledby="mcpItemConfigModalTitle">
2838
+ <div class="agent-md-modal-head">
2839
+ <h2 id="mcpItemConfigModalTitle">编辑 MCP 配置</h2>
2840
+ <button type="button" class="agent-md-modal-close" id="btnMcpItemConfigModalClose" aria-label="关闭">
2841
+ &times;
2842
+ </button>
2843
+ </div>
2844
+ <p id="mcpItemConfigHint" class="tool-files-sub" style="margin: 0 1rem 0.5rem; line-height: 1.4">
2845
+ 标准 mcp.json 单条 server 配置(与 Cursor mcp.json 相同):<code>{ "command": "...", "args": [...], "env": {...} }</code>。保存后写入 agent .mcp.json 的 <code>mcpServers.&lt;serverName&gt;</code>。
2846
+ </p>
2847
+ <textarea id="mcpItemConfigEditor" class="agent-md-editor" spellcheck="false" placeholder="加载中…"></textarea>
2848
+ <div class="agent-md-modal-foot">
2849
+ <button type="button" class="btn-agent-md-cancel" id="btnMcpItemConfigCancel">取消</button>
2850
+ <button type="button" class="btn-primary" id="btnMcpItemConfigSave">保存</button>
2851
+ </div>
2852
+ </div>
2853
+ </div>
2854
+
2835
2855
  <div id="agentMdModal" class="agent-md-modal" aria-hidden="true">
2836
2856
  <div class="agent-md-modal-backdrop" id="agentMdModalBackdrop"></div>
2837
2857
  <div class="agent-md-modal-card" role="dialog" aria-modal="true" aria-labelledby="agentMdModalTitle">
@@ -3452,6 +3472,14 @@
3452
3472
  const btnMcpJsonInputModalClose = $('btnMcpJsonInputModalClose')
3453
3473
  const btnMcpJsonInputCancel = $('btnMcpJsonInputCancel')
3454
3474
  const btnMcpJsonInputConfirm = $('btnMcpJsonInputConfirm')
3475
+ const mcpItemConfigModal = $('mcpItemConfigModal')
3476
+ const mcpItemConfigModalBackdrop = $('mcpItemConfigModalBackdrop')
3477
+ const mcpItemConfigModalTitle = $('mcpItemConfigModalTitle')
3478
+ const mcpItemConfigEditor = $('mcpItemConfigEditor')
3479
+ const btnMcpItemConfigModalClose = $('btnMcpItemConfigModalClose')
3480
+ const btnMcpItemConfigCancel = $('btnMcpItemConfigCancel')
3481
+ const btnMcpItemConfigSave = $('btnMcpItemConfigSave')
3482
+ let mcpItemEditState = null
3455
3483
 
3456
3484
  ;(function initAgentIdFromPageUrl() {
3457
3485
  try {
@@ -3889,13 +3917,29 @@
3889
3917
  }
3890
3918
  }
3891
3919
 
3892
- function resolveEnvelopeSourceAgent(runId, payload) {
3920
+ /**
3921
+ * 业务语义:解析消息所属 agent 展示名。
3922
+ * 优先 payload.sourceAgent,其次 v3 信封 agent_name,main run 再回退页面当前 agent。
3923
+ */
3924
+ function resolveEnvelopeSourceAgent(runId, payload, envelopeAgentName) {
3893
3925
  const payloadSource =
3894
3926
  payload && typeof payload.sourceAgent === 'string'
3895
- ? payload.sourceAgent
3927
+ ? payload.sourceAgent.trim()
3896
3928
  : ''
3897
3929
  if (payloadSource) return payloadSource
3898
- return runId === 'main' ? 'main' : 'unknown-agent'
3930
+ const envelopeSource =
3931
+ typeof envelopeAgentName === 'string' ? envelopeAgentName.trim() : ''
3932
+ if (envelopeSource) return envelopeSource
3933
+ if (runId === 'main') return currentAgentParam()
3934
+ return 'unknown-agent'
3935
+ }
3936
+
3937
+ function resolveRunDisplayAgent(runId, sourceAgent) {
3938
+ const named =
3939
+ typeof sourceAgent === 'string' ? sourceAgent.trim() : ''
3940
+ if (named) return named
3941
+ if (runId === 'main') return currentAgentParam()
3942
+ return 'unknown-agent'
3899
3943
  }
3900
3944
 
3901
3945
  function ensureRunBuffer(runId) {
@@ -4249,7 +4293,7 @@
4249
4293
  Number.isInteger(item.arrivalSeq) ? item.arrivalSeq : Number.MAX_SAFE_INTEGER,
4250
4294
  fallbackOrder: timelineFallbackOrder++,
4251
4295
  runId: runId,
4252
- sourceAgent: item.sourceAgent || (runId === 'main' ? 'main' : 'unknown-agent'),
4296
+ sourceAgent: resolveRunDisplayAgent(runId, item.sourceAgent),
4253
4297
  scheduledTrigger: item.scheduledTrigger === true,
4254
4298
  kind: 'conversation',
4255
4299
  payload: item,
@@ -4269,7 +4313,7 @@
4269
4313
  Number.isInteger(item.arrivalSeq) ? item.arrivalSeq : Number.MAX_SAFE_INTEGER,
4270
4314
  fallbackOrder: timelineFallbackOrder++,
4271
4315
  runId: rid,
4272
- sourceAgent: item.source || (rid === 'main' ? 'main' : 'unknown-agent'),
4316
+ sourceAgent: resolveRunDisplayAgent(rid, item.source),
4273
4317
  scheduledTrigger: item.scheduledTrigger === true,
4274
4318
  kind: 'source',
4275
4319
  payload: item,
@@ -4333,7 +4377,7 @@
4333
4377
  const runId = block.runId
4334
4378
  const blockItems = block.items
4335
4379
  if (!blockItems.length) continue
4336
- const defaultSourceAgent = block.sourceAgent || (runId === 'main' ? 'main' : 'unknown-agent')
4380
+ const defaultSourceAgent = resolveRunDisplayAgent(runId, block.sourceAgent)
4337
4381
  const mainSegmentSuffix =
4338
4382
  block.isMainSegment && block.segmentSeq > 1 ? ' · segment=' + block.segmentSeq : ''
4339
4383
  const lastBlockItem = blockItems[blockItems.length - 1]
@@ -4527,7 +4571,7 @@
4527
4571
  const action = typeof payload.action === 'string' ? payload.action : ''
4528
4572
  const index = Number.isInteger(payload.index) ? payload.index : -1
4529
4573
  const msg = payload.message
4530
- const sourceAgent = resolveEnvelopeSourceAgent(runId, payload)
4574
+ const sourceAgent = resolveEnvelopeSourceAgent(runId, payload, data.agent_name)
4531
4575
  const runBuffer = ensureRunBuffer(runId)
4532
4576
  const runMessage = {
4533
4577
  message: msg,
@@ -5463,6 +5507,54 @@
5463
5507
  return u.toString()
5464
5508
  }
5465
5509
 
5510
+ function buildRuntimeMcpItemConfigUrl(itemId, source) {
5511
+ const u = new URL('/api/runtime/mcps/item-config', httpOriginForApi() + '/')
5512
+ u.searchParams.set('agent', currentAgentParam())
5513
+ u.searchParams.set('item', itemId)
5514
+ u.searchParams.set('source', source)
5515
+ return u.toString()
5516
+ }
5517
+
5518
+ function closeMcpItemConfigModal() {
5519
+ if (!mcpItemConfigModal) return
5520
+ mcpItemConfigModal.classList.remove('is-open')
5521
+ mcpItemConfigModal.setAttribute('aria-hidden', 'true')
5522
+ mcpItemEditState = null
5523
+ }
5524
+
5525
+ function openMcpItemConfigEditor(itemId, source, onSaved) {
5526
+ if (!mcpItemConfigModal || !mcpItemConfigEditor) return
5527
+ mcpItemEditState = { itemId: itemId, source: source, packageId: null, onSaved: onSaved || null }
5528
+ if (mcpItemConfigModalTitle) {
5529
+ mcpItemConfigModalTitle.textContent = '编辑 MCP 配置 · ' + itemId
5530
+ }
5531
+ mcpItemConfigEditor.value = '加载中…'
5532
+ mcpItemConfigModal.classList.add('is-open')
5533
+ mcpItemConfigModal.setAttribute('aria-hidden', 'false')
5534
+ fetch(buildRuntimeMcpItemConfigUrl(itemId, source))
5535
+ .then(function (r) {
5536
+ return r.json().then(function (body) {
5537
+ return { r: r, body: body }
5538
+ })
5539
+ })
5540
+ .then(function (x) {
5541
+ if (!x.r.ok) throw new Error(x.body.message || x.body.error || x.r.statusText)
5542
+ if (mcpItemEditState) {
5543
+ mcpItemEditState.packageId = x.body.packageId || null
5544
+ }
5545
+ const overlay = x.body.overlay
5546
+ if (overlay && typeof overlay === 'object' && !Array.isArray(overlay)) {
5547
+ mcpItemConfigEditor.value = JSON.stringify(overlay, null, 2)
5548
+ } else {
5549
+ mcpItemConfigEditor.value = '{}'
5550
+ }
5551
+ })
5552
+ .catch(function (e) {
5553
+ window.alert('加载 MCP 配置失败: ' + e)
5554
+ closeMcpItemConfigModal()
5555
+ })
5556
+ }
5557
+
5466
5558
  function buildRuntimeSkillFilesUrl() {
5467
5559
  const u = new URL('/api/runtime/skills/files', httpOriginForApi() + '/')
5468
5560
  u.searchParams.set('agent', currentAgentParam())
@@ -5723,7 +5815,17 @@
5723
5815
  (hasJson ? ' · 含 .mcp.json' : ' · 缺少 .mcp.json') +
5724
5816
  ' · 可导出文件 ' +
5725
5817
  (Number.isFinite(fc) && fc > 0 ? fc : 0) +
5726
- ' · zip 仅含编译产物(mcp.json / index.js)'
5818
+ ' · zip 为单包目录整包解压'
5819
+ const editBtn = document.createElement('button')
5820
+ editBtn.type = 'button'
5821
+ editBtn.className = 'tool-files-download'
5822
+ editBtn.textContent = '编辑'
5823
+ editBtn.style.marginRight = '0.45rem'
5824
+ editBtn.addEventListener('click', function () {
5825
+ openMcpItemConfigEditor(file, 'runtime', function () {
5826
+ void loadMcpPackageList()
5827
+ })
5828
+ })
5727
5829
  const exportBtn = document.createElement('button')
5728
5830
  exportBtn.type = 'button'
5729
5831
  exportBtn.className = 'tool-files-download'
@@ -7781,10 +7883,26 @@
7781
7883
  }
7782
7884
  exportBtn.addEventListener('click', function () {
7783
7885
  if (exportBtn.disabled) return
7784
- window.open(buildRuntimeMcpExportUrl(id), '_blank', 'noopener')
7886
+ const pkgId = row.s && row.s.packageId ? String(row.s.packageId) : id
7887
+ window.open(buildRuntimeMcpExportUrl(pkgId), '_blank', 'noopener')
7785
7888
  })
7786
7889
  actions.appendChild(exportBtn)
7787
7890
  }
7891
+ const editBtn = document.createElement('button')
7892
+ editBtn.type = 'button'
7893
+ editBtn.className = 'btn-picker-action'
7894
+ editBtn.textContent = '编辑'
7895
+ editBtn.addEventListener('click', function () {
7896
+ const src = source === 'remote' ? 'remote' : 'runtime'
7897
+ openMcpItemConfigEditor(id, src, function () {
7898
+ if (typeof refreshMcpPickerRows === 'function') {
7899
+ void refreshMcpPickerRows().then(function () {
7900
+ renderMcpRows()
7901
+ })
7902
+ }
7903
+ })
7904
+ })
7905
+ actions.appendChild(editBtn)
7788
7906
  const toggleBtn = document.createElement('button')
7789
7907
  toggleBtn.type = 'button'
7790
7908
  toggleBtn.className = 'btn-picker-action'
@@ -7881,7 +7999,8 @@
7881
7999
  })
7882
8000
  return
7883
8001
  }
7884
- fetch(buildRuntimeMcpDeleteUrl(id), { method: 'DELETE' })
8002
+ const pkgId = row.s && row.s.packageId ? String(row.s.packageId) : id
8003
+ fetch(buildRuntimeMcpDeleteUrl(pkgId), { method: 'DELETE' })
7885
8004
  .then(function (r) {
7886
8005
  return r.json().catch(function () {
7887
8006
  return {}
@@ -8542,6 +8661,69 @@
8542
8661
  if (mcpJsonInputModalBackdrop) {
8543
8662
  mcpJsonInputModalBackdrop.addEventListener('click', closeMcpJsonInputModal)
8544
8663
  }
8664
+ if (btnMcpItemConfigModalClose) {
8665
+ btnMcpItemConfigModalClose.addEventListener('click', closeMcpItemConfigModal)
8666
+ }
8667
+ if (btnMcpItemConfigCancel) {
8668
+ btnMcpItemConfigCancel.addEventListener('click', closeMcpItemConfigModal)
8669
+ }
8670
+ if (mcpItemConfigModalBackdrop) {
8671
+ mcpItemConfigModalBackdrop.addEventListener('click', closeMcpItemConfigModal)
8672
+ }
8673
+ if (btnMcpItemConfigSave && mcpItemConfigEditor) {
8674
+ btnMcpItemConfigSave.addEventListener('click', function () {
8675
+ if (!mcpItemEditState) return
8676
+ const raw = String(mcpItemConfigEditor.value || '').trim()
8677
+ let server
8678
+ try {
8679
+ server = raw ? JSON.parse(raw) : {}
8680
+ } catch (e) {
8681
+ window.alert('JSON 解析失败: ' + e)
8682
+ return
8683
+ }
8684
+ if (!server || typeof server !== 'object' || Array.isArray(server)) {
8685
+ window.alert('配置必须是 JSON 对象')
8686
+ return
8687
+ }
8688
+ if (server.mcpServers) {
8689
+ window.alert('请填写标准 server 配置(command/args/env),不要嵌套 mcpServers')
8690
+ return
8691
+ }
8692
+ const clearOverlay = Object.keys(server).length === 0
8693
+ btnMcpItemConfigSave.disabled = true
8694
+ const saveUrl = buildRuntimeMcpItemConfigUrl(mcpItemEditState.itemId, mcpItemEditState.source)
8695
+ const saveOpts = clearOverlay
8696
+ ? { method: 'DELETE' }
8697
+ : {
8698
+ method: 'PUT',
8699
+ headers: { 'Content-Type': 'application/json; charset=utf-8' },
8700
+ body: JSON.stringify({ server: server }),
8701
+ }
8702
+ fetch(saveUrl, saveOpts)
8703
+ .then(function (r) {
8704
+ return r.json().then(function (body) {
8705
+ return { r: r, body: body }
8706
+ })
8707
+ })
8708
+ .then(function (x) {
8709
+ if (!x.r.ok) throw new Error(x.body.message || x.body.error || x.r.statusText)
8710
+ closeMcpItemConfigModal()
8711
+ setStatus('MCP 配置已保存', serverReady ? 'ready' : '')
8712
+ return persistAgentConfigAndRestartSession({ silent: true })
8713
+ })
8714
+ .then(function () {
8715
+ if (mcpItemEditState && typeof mcpItemEditState.onSaved === 'function') {
8716
+ mcpItemEditState.onSaved()
8717
+ }
8718
+ })
8719
+ .catch(function (e) {
8720
+ window.alert('保存失败: ' + e)
8721
+ })
8722
+ .finally(function () {
8723
+ btnMcpItemConfigSave.disabled = false
8724
+ })
8725
+ })
8726
+ }
8545
8727
  if (btnMcpJsonInputConfirm && mcpJsonInputEditor) {
8546
8728
  btnMcpJsonInputConfirm.addEventListener('click', function () {
8547
8729
  const raw = String(mcpJsonInputEditor.value || '').trim()
@@ -1,23 +1,15 @@
1
1
  /**
2
- * agent 启动环境:默认路径 + .env + 内置 runtime 同步到 .ws-agents。
2
+ * agent 启动环境:默认路径 + .env 加载。
3
3
  * 发布时由 sync-launcher-env 复制为 packages/agent/lib/launcher-env.js(单文件,无子模块依赖)。
4
4
  */
5
5
 
6
- import { cpSync, existsSync, mkdirSync, readFileSync } from 'node:fs'
6
+ import { existsSync, readFileSync } from 'node:fs'
7
7
  import { homedir } from 'node:os'
8
8
  import { dirname, join } from 'node:path'
9
- import { fileURLToPath } from 'node:url'
10
9
  import { createRequire } from 'node:module'
11
10
 
12
11
  const require = createRequire(import.meta.url)
13
12
 
14
- /** 与 prepack、wsSkillRuntime 等保持一致的内置 runtime 目录名 */
15
- const BUILTIN_RUNTIME_DIR_NAMES = [
16
- 'skills-runtime',
17
- 'tools-runtime',
18
- 'mcps-runtime',
19
- ]
20
-
21
13
  export function resolveMainPackageRoot() {
22
14
  try {
23
15
  return dirname(require.resolve('@auto-ai/agent/package.json'))
@@ -26,18 +18,6 @@ export function resolveMainPackageRoot() {
26
18
  }
27
19
  }
28
20
 
29
- /**
30
- * 内置 runtime 来源根:已安装的 @auto-ai/agent 包根,或源码仓库根。
31
- */
32
- export function resolveBundledRuntimeSourceRoot() {
33
- try {
34
- const pkgJson = require.resolve('@auto-ai/agent/package.json')
35
- return dirname(pkgJson)
36
- } catch {
37
- return join(dirname(fileURLToPath(import.meta.url)), '..')
38
- }
39
- }
40
-
41
21
  /**
42
22
  * 解析 WS_AGENTS_ROOT:显式环境变量优先,否则 <WS_PROJECT_ROOT|cwd>/.ws-agents。
43
23
  */
@@ -50,34 +30,6 @@ export function resolveWsAgentsRoot(env) {
50
30
  return join(projectRoot, '.ws-agents')
51
31
  }
52
32
 
53
- /**
54
- * 若 .ws-agents 下尚无对应 runtime 目录,则从包内/仓库根复制一份。
55
- * 已存在则跳过,避免覆盖用户通过 API 安装或手工改动的内容。
56
- */
57
- export function ensureWsAgentsBuiltinRuntime(env) {
58
- const agentsRoot = resolveWsAgentsRoot(env)
59
- if (!env.WS_AGENTS_ROOT?.trim()) {
60
- env.WS_AGENTS_ROOT = agentsRoot
61
- }
62
-
63
- const sourceRoot = resolveBundledRuntimeSourceRoot()
64
- mkdirSync(agentsRoot, { recursive: true })
65
-
66
- for (const dirName of BUILTIN_RUNTIME_DIR_NAMES) {
67
- const src = join(sourceRoot, dirName)
68
- const dest = join(agentsRoot, dirName)
69
- if (!existsSync(src) || existsSync(dest)) {
70
- continue
71
- }
72
- cpSync(src, dest, {
73
- recursive: true,
74
- filter: path => !path.split(/[/\\]+/).includes('.ws-agent'),
75
- })
76
- }
77
-
78
- return env
79
- }
80
-
81
33
  function applyDotenvLines(env, text) {
82
34
  for (const line of text.split('\n')) {
83
35
  const trimmed = line.trim()
@@ -128,7 +80,11 @@ export function buildLauncherEnv(baseEnv = process.env) {
128
80
  loadDotenvFile(env, join(homedir(), '.config', 'agent', 'env'))
129
81
  loadDotenvFile(env, join(homedir(), '.agent.env'))
130
82
 
131
- return ensureWsAgentsBuiltinRuntime(env)
83
+ if (!env.WS_AGENTS_ROOT?.trim()) {
84
+ env.WS_AGENTS_ROOT = resolveWsAgentsRoot(env)
85
+ }
86
+
87
+ return env
132
88
  }
133
89
 
134
90
  /** 写入当前进程环境(源码直接启动 wsServer 时使用) */
@@ -14,6 +14,17 @@ log(`[BOOT] tuitui-channel boot.mjs start pid=${process.pid}`);
14
14
  log(`[BOOT] marker=2026-03-23-v2-runtime-probe`);
15
15
  log(`[BOOT] env TUITUI_DM_POLICY=${process.env.TUITUI_DM_POLICY || ''} TUITUI_STATE_DIR=${process.env.TUITUI_STATE_DIR || ''}`);
16
16
 
17
+ // 父进程(WS 会话子进程 / bun -e 调试)退出后,stdio MCP 不应继续存活成为孤儿。
18
+ const parentPidAtBoot = process.ppid;
19
+ const parentExitWatch = setInterval(() => {
20
+ if (process.ppid !== parentPidAtBoot) {
21
+ clearInterval(parentExitWatch);
22
+ log(`[BOOT] parent pid changed ${parentPidAtBoot} -> ${process.ppid}, exiting`);
23
+ process.exit(0);
24
+ }
25
+ }, 1000);
26
+ parentExitWatch.unref?.();
27
+
17
28
  process.on('uncaughtException', (err) => {
18
29
  log(`[BOOT] uncaughtException ${err?.stack || err}`);
19
30
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auto-ai/agent",
3
- "version": "2.1.102",
3
+ "version": "2.1.104",
4
4
  "description": "Auto AI Agent 网关 CLI(WebSocket,独立二进制,无需 Bun)",
5
5
  "type": "module",
6
6
  "maintainers": [
@@ -27,11 +27,11 @@
27
27
  "node": ">=18"
28
28
  },
29
29
  "optionalDependencies": {
30
- "@auto-ai/agent-linux-x64": "2.1.102",
31
- "@auto-ai/agent-linux-arm64": "2.1.102",
32
- "@auto-ai/agent-darwin-x64": "2.1.102",
33
- "@auto-ai/agent-darwin-arm64": "2.1.102",
34
- "@auto-ai/agent-win-x64": "2.1.102"
30
+ "@auto-ai/agent-linux-x64": "2.1.104",
31
+ "@auto-ai/agent-linux-arm64": "2.1.104",
32
+ "@auto-ai/agent-darwin-x64": "2.1.104",
33
+ "@auto-ai/agent-darwin-arm64": "2.1.104",
34
+ "@auto-ai/agent-win-x64": "2.1.104"
35
35
  },
36
36
  "scripts": {
37
37
  "prepare": "node ../../scripts/sync-launcher-env.js",