@ai_mind/dsh-a2a 1.2.0 → 1.3.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/README.md +10 -1
- package/client.js +5 -0
- package/lib/bridge.js +45 -0
- package/lib/config.js +4 -0
- package/lib/panel.js +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
面板分三部分:
|
|
116
116
|
|
|
117
117
|
1. **概览**:实时 Agent Card、JSON-RPC / HTTP+JSON 地址(一键复制)、流式/推送/扩展卡片能力、鉴权状态、运行状态与可复制的 `curl` / 官方 JS 客户端示例。
|
|
118
|
-
2. **配置表单**:直接编辑名称、描述、对外地址、挂载路径、流式/推送/扩展卡片、鉴权模式与令牌环境变量、访问令牌(写入后不再回显)、Agent
|
|
118
|
+
2. **配置表单**:直接编辑名称、描述、对外地址、挂载路径、流式/推送/扩展卡片、鉴权模式与令牌环境变量、访问令牌(写入后不再回显)、Agent 预设/模型/工作目录/工作区名称/沙箱/审批/并发/超时,以及请求体与任务保留限额。预设、模型提供方、模型和推理强度会从当前 Host 的 `agentPresets` / `llm` 服务动态列出选项;留空即使用部署默认,无法列出的自定义值仍可手动输入。
|
|
119
119
|
3. **保存**:写入 profile 的 `cordis.patch.yml`(`a2a-server` 行),校验通过后插件自动重载;修改挂载路径后刷新页面即可。
|
|
120
120
|
|
|
121
121
|
配置接口 `GET/PUT <mountPath>/panel/config` 始终位于 DSH Web 的 origin(独立端口模式下也不在 A2A 端口上),使用 **Harness Web 连接凭据**(不是 A2A token)鉴权:只有能打开 Settings 的已登录浏览器可以读写,远程 A2A 客户端即使知道路径也会收到 401。令牌字段是只写字段,GET 只返回“是否已配置”。
|
|
@@ -266,6 +266,14 @@ server:
|
|
|
266
266
|
|
|
267
267
|
---
|
|
268
268
|
|
|
269
|
+
### 工作区分组
|
|
270
|
+
|
|
271
|
+
A2A 创建的 Harness 会话使用 `agent.cwd` 作为工作目录,GUI 按会话目录把会话归入已注册的 Workspace。如果该目录还不是工作区,会话就会显示在「未分组」。
|
|
272
|
+
|
|
273
|
+
- 想让 A2A 会话归入**已有工作区**:把 `agent.cwd` 指向该工作区目录即可,插件会复用该工作区并附加新会话。
|
|
274
|
+
- 想为专用目录**新建分组**:设置 `agent.workspaceTitle`(面板里的「工作区名称(自动分组)」),插件会在该目录注册一个同名工作区,并把之后创建的 A2A 会话附加进去。
|
|
275
|
+
- 已有历史会话不会自动追溯重排;新创建的 A2A 任务会出现在分组下。
|
|
276
|
+
|
|
269
277
|
## Agent 运行时映射
|
|
270
278
|
|
|
271
279
|
### 会话与并发
|
|
@@ -400,6 +408,7 @@ agent:
|
|
|
400
408
|
reasoningEffort: null
|
|
401
409
|
maxTokens: null
|
|
402
410
|
cwd: null # 绝对路径;留空用 workingDirectory.defaultDirectory
|
|
411
|
+
workspaceTitle: null # 可选;为 cwd 注册 GUI 工作区并让 A2A 会话归入该分组
|
|
403
412
|
sandboxMode: workspace-write
|
|
404
413
|
approvalPolicy: never
|
|
405
414
|
maxConcurrentTasks: 4
|
package/client.js
CHANGED
|
@@ -108,9 +108,11 @@ window.__ModuleLoader__.load({
|
|
|
108
108
|
fieldAgentProvider: '模型提供方',
|
|
109
109
|
fieldAgentModel: '模型',
|
|
110
110
|
fieldAgentReasoningEffort: '推理强度',
|
|
111
|
+
fieldAgentWorkspaceTitle: '工作区名称(自动分组)',
|
|
111
112
|
optionDefault: '(使用默认)',
|
|
112
113
|
defaultModel: '默认模型(留空时)',
|
|
113
114
|
modelPairHint: '模型提供方和模型必须同时选择,或都留空使用默认。',
|
|
115
|
+
workspaceHint: 'A2A 会话会归入 agent.cwd 对应的工作区;若该目录还不是工作区,填写“工作区名称”即可为该目录创建分组。',
|
|
114
116
|
fieldAgentSandboxMode: '沙箱模式',
|
|
115
117
|
fieldAgentApprovalPolicy: '审批策略',
|
|
116
118
|
fieldAgentMaxConcurrentTasks: '最大并发任务',
|
|
@@ -223,9 +225,11 @@ window.__ModuleLoader__.load({
|
|
|
223
225
|
fieldAgentProvider: 'Model provider',
|
|
224
226
|
fieldAgentModel: 'Model',
|
|
225
227
|
fieldAgentReasoningEffort: 'Reasoning effort',
|
|
228
|
+
fieldAgentWorkspaceTitle: 'Workspace title (auto-group)',
|
|
226
229
|
optionDefault: '(use default)',
|
|
227
230
|
defaultModel: 'Default model (when empty)',
|
|
228
231
|
modelPairHint: 'Choose both a provider and a model, or leave both empty for the default.',
|
|
232
|
+
workspaceHint: 'A2A sessions join the workspace that owns agent.cwd. Set a workspace title to create that workspace when the directory is not registered yet.',
|
|
229
233
|
fieldAgentSandboxMode: 'Sandbox mode',
|
|
230
234
|
fieldAgentApprovalPolicy: 'Approval policy',
|
|
231
235
|
fieldAgentMaxConcurrentTasks: 'Max concurrent tasks',
|
|
@@ -821,6 +825,7 @@ window.__ModuleLoader__.load({
|
|
|
821
825
|
(optionSets.default.reasoningEffort ? ` · ${optionSets.default.reasoningEffort}` : ''),
|
|
822
826
|
)
|
|
823
827
|
: null,
|
|
828
|
+
group === 'agent' ? h('p', { className: 'a2aui-field-note' }, t('workspaceHint')) : null,
|
|
824
829
|
)
|
|
825
830
|
return group === 'auth' ? [node, secretBlock] : [node]
|
|
826
831
|
}),
|
package/lib/bridge.js
CHANGED
|
@@ -275,6 +275,7 @@ class ContextWorker {
|
|
|
275
275
|
throw new Error(`A2A agent cwd must be an absolute path, got ${JSON.stringify(cwd)}`)
|
|
276
276
|
}
|
|
277
277
|
await mkdir(cwd, { recursive: true })
|
|
278
|
+
const workspace = await runtime.ensureWorkspace(cwd)
|
|
278
279
|
const sessionId = `session-a2a-${newId()}`
|
|
279
280
|
const agentOptions = runtime.agentOptions()
|
|
280
281
|
const presets = runtime.ctx.get?.('agentPresets')
|
|
@@ -307,6 +308,13 @@ class ContextWorker {
|
|
|
307
308
|
}
|
|
308
309
|
this.#handle = handle
|
|
309
310
|
this.#agent = handle.agent
|
|
311
|
+
if (workspace !== null) {
|
|
312
|
+
try {
|
|
313
|
+
await workspace.attachSession(handle.agent.id)
|
|
314
|
+
} catch (error) {
|
|
315
|
+
runtime.logger.warn(`[a2a] could not attach session ${handle.agent.id} to workspace ${workspace.path}: ${errorMessage(error)}`)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
310
318
|
try {
|
|
311
319
|
runtime.applyAgentPolicy(handle.agent)
|
|
312
320
|
} catch (error) {
|
|
@@ -361,6 +369,7 @@ export class AgentRuntime {
|
|
|
361
369
|
#logger
|
|
362
370
|
#workers = new Map()
|
|
363
371
|
#sweepTimer = undefined
|
|
372
|
+
#workspacePromise = undefined
|
|
364
373
|
#disposed = false
|
|
365
374
|
#stats = { turns: 0, completed: 0, failed: 0, canceled: 0 }
|
|
366
375
|
|
|
@@ -422,6 +431,42 @@ export class AgentRuntime {
|
|
|
422
431
|
return options
|
|
423
432
|
}
|
|
424
433
|
|
|
434
|
+
/**
|
|
435
|
+
* Resolve the project (GUI workspace) that owns the agent working directory.
|
|
436
|
+
*
|
|
437
|
+
* An existing workspace is reused and its sessions are attached explicitly.
|
|
438
|
+
* When `agent.workspaceTitle` is set, a missing workspace is created for the
|
|
439
|
+
* directory so the GUI can group new A2A sessions under it.
|
|
440
|
+
*
|
|
441
|
+
* @param {string} cwd absolute agent working directory
|
|
442
|
+
* @returns {Promise<object|null>} the Workspace entity, or null when grouping is unavailable
|
|
443
|
+
*/
|
|
444
|
+
async ensureWorkspace(cwd) {
|
|
445
|
+
if (this.#workspacePromise === undefined) {
|
|
446
|
+
this.#workspacePromise = (async () => {
|
|
447
|
+
const registry = this.#ctx.get?.('workspaceRegistry')
|
|
448
|
+
if (registry === undefined) {
|
|
449
|
+
if (this.#config.agent.workspaceTitle) {
|
|
450
|
+
this.#logger.warn('[a2a] agent.workspaceTitle is set but workspaceRegistry is unavailable')
|
|
451
|
+
}
|
|
452
|
+
return null
|
|
453
|
+
}
|
|
454
|
+
try {
|
|
455
|
+
const existing = await registry.resolveByPath(cwd)
|
|
456
|
+
if (existing !== undefined) return existing
|
|
457
|
+
if (!this.#config.agent.workspaceTitle) return null
|
|
458
|
+
const created = await registry.create(cwd, this.#config.agent.workspaceTitle)
|
|
459
|
+
this.#logger.info(`[a2a] workspace "${created.title}" registered for ${cwd}`)
|
|
460
|
+
return created
|
|
461
|
+
} catch (error) {
|
|
462
|
+
this.#logger.warn(`[a2a] cannot resolve or create a workspace for ${cwd}: ${errorMessage(error)}`)
|
|
463
|
+
return null
|
|
464
|
+
}
|
|
465
|
+
})()
|
|
466
|
+
}
|
|
467
|
+
return this.#workspacePromise
|
|
468
|
+
}
|
|
469
|
+
|
|
425
470
|
/** @returns {Promise<string>} the absolute workspace directory for agents */
|
|
426
471
|
async resolveCwd() {
|
|
427
472
|
if (this.#config.agent.cwd) return this.#config.agent.cwd
|
package/lib/config.js
CHANGED
|
@@ -96,6 +96,7 @@ export const DEFAULT_CONFIG = {
|
|
|
96
96
|
reasoningEffort: null,
|
|
97
97
|
maxTokens: null,
|
|
98
98
|
cwd: null,
|
|
99
|
+
workspaceTitle: null,
|
|
99
100
|
sandboxMode: 'workspace-write',
|
|
100
101
|
approvalPolicy: 'never',
|
|
101
102
|
maxConcurrentTasks: 4,
|
|
@@ -463,6 +464,9 @@ export function normalizeConfig(raw) {
|
|
|
463
464
|
if (merged.agent.cwd !== null && !merged.agent.cwd.startsWith('/')) {
|
|
464
465
|
fail('agent.cwd', 'must be an absolute path')
|
|
465
466
|
}
|
|
467
|
+
merged.agent.workspaceTitle = expectNullableString(merged.agent.workspaceTitle, 'agent.workspaceTitle', {
|
|
468
|
+
allowEmpty: false,
|
|
469
|
+
})
|
|
466
470
|
merged.agent.sandboxMode = expectEnum(merged.agent.sandboxMode, 'agent.sandboxMode', [
|
|
467
471
|
'inherit',
|
|
468
472
|
'read-only',
|
package/lib/panel.js
CHANGED
|
@@ -49,6 +49,7 @@ const FIELDS = [
|
|
|
49
49
|
{ path: 'auth.tokenEnv', type: 'string', group: 'auth', nullable: true, placeholder: 'A2A_AUTH_TOKEN' },
|
|
50
50
|
{ path: 'auth.allowRemoteUnauthenticated', type: 'boolean', group: 'auth' },
|
|
51
51
|
{ path: 'agent.cwd', type: 'string', group: 'agent', nullable: true, placeholder: '/srv/a2a-workspace' },
|
|
52
|
+
{ path: 'agent.workspaceTitle', type: 'string', group: 'agent', nullable: true, placeholder: 'A2A Agents' },
|
|
52
53
|
{ path: 'agent.preset', type: 'string', group: 'agent', nullable: true, optionsKey: 'presets' },
|
|
53
54
|
{ path: 'agent.provider', type: 'string', group: 'agent', nullable: true, optionsKey: 'providers' },
|
|
54
55
|
{ path: 'agent.model', type: 'string', group: 'agent', nullable: true, optionsKey: 'models' },
|
|
@@ -305,7 +306,7 @@ export function createPanelRouter({ ctx, config, mountPath, logger, status, heal
|
|
|
305
306
|
res.setHeader('Cache-Control', 'no-store')
|
|
306
307
|
res.json({
|
|
307
308
|
ok: true,
|
|
308
|
-
plugin: { name: PLUGIN_NAME, version: '1.
|
|
309
|
+
plugin: { name: PLUGIN_NAME, version: '1.3.0' },
|
|
309
310
|
mountPath,
|
|
310
311
|
...payload,
|
|
311
312
|
...(typeof status === 'function' ? { server: status() } : {}),
|
|
@@ -484,7 +485,7 @@ export function createPanelRouter({ ctx, config, mountPath, logger, status, heal
|
|
|
484
485
|
logger.info('[a2a] configuration updated from the settings panel')
|
|
485
486
|
res.json({
|
|
486
487
|
ok: true,
|
|
487
|
-
plugin: { name: PLUGIN_NAME, version: '1.
|
|
488
|
+
plugin: { name: PLUGIN_NAME, version: '1.3.0' },
|
|
488
489
|
mountPath,
|
|
489
490
|
...describe(live, liveEntry, []),
|
|
490
491
|
...(typeof status === 'function' ? { server: status() } : {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai_mind/dsh-a2a",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Production A2A (Agent2Agent) v1.0 server for DeepSeek Harness agents: JSON-RPC + HTTP+JSON bindings, streaming, tasks, push notifications and signed Agent Cards.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|