dsh-agentic-tool-catalog 0.0.0-stage → 0.3.2
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/CHANGELOG.md +63 -0
- package/LICENSE +21 -0
- package/README.md +173 -2
- package/README.zh.md +135 -0
- package/cordis.patch.yml +29 -0
- package/docs/assets/plugin-page.png +0 -0
- package/docs/assets/ui-dock.png +0 -0
- package/docs/assets/ui-panel.png +0 -0
- package/docs/benchmark.md +54 -0
- package/docs/data-card.md +96 -0
- package/docs/detail-benchmark.svg +30 -0
- package/docs/detail-hero.svg +57 -0
- package/docs/detail.md +33 -0
- package/docs/detail.zh.md +30 -0
- package/icon.svg +15 -0
- package/lib/catalog.js +208 -0
- package/lib/client.js +368 -0
- package/lib/generation.js +342 -0
- package/lib/index.js +795 -0
- package/lib/store.js +108 -0
- package/lib/subagent-compat.js +87 -0
- package/locale/en.json +7 -0
- package/locale/zh.json +7 -0
- package/package.json +90 -4
package/lib/store.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* store.js —— 目录持久化:只使用 DSH 官方 Storage(storageDomain)。
|
|
3
|
+
*
|
|
4
|
+
* 数据是派生缓存(丢了重新生成一次即可),但按用户决策统一走官方持久化:
|
|
5
|
+
* - 声明 domain `agentic-tool-catalog`(version 1):`catalogs` 表按目录指纹存原始分组,
|
|
6
|
+
* `state` 表存最近一次成功分组(lastGood);
|
|
7
|
+
* - 没有 `storageDomain` 服务的 profile → 纯内存缓存(功能可用,重启重新生成一次),
|
|
8
|
+
* **不做第二份文件后端**;
|
|
9
|
+
* - 写入 fire-and-forget:失败只记日志,不影响当前会话;读取在启动时 hydrate 到共享内存。
|
|
10
|
+
*
|
|
11
|
+
* @module @local/dsh-agentic-tool-catalog/store
|
|
12
|
+
*/
|
|
13
|
+
import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'
|
|
14
|
+
import { z } from 'zod'
|
|
15
|
+
|
|
16
|
+
/** 一个分组(模型输出的原始形状,tools 为工具名;读取时复用 generation.validateAgentGroups 校验)。 */
|
|
17
|
+
const groupRecord = z.object({
|
|
18
|
+
id: z.string(),
|
|
19
|
+
description: z.string(),
|
|
20
|
+
tools: z.array(z.string()),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
/** 按目录指纹存的一版分组。 */
|
|
24
|
+
const catalogRecord = z.object({
|
|
25
|
+
fingerprint: z.string(),
|
|
26
|
+
groups: z.array(groupRecord),
|
|
27
|
+
createdAt: z.number(),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
/** 最近一次成功分组(生成失败时保持目录连续;状态会显式标 stale,不静默)。 */
|
|
31
|
+
const lastGoodRecord = z.object({
|
|
32
|
+
fingerprint: z.string(),
|
|
33
|
+
groups: z.array(groupRecord),
|
|
34
|
+
at: z.number(),
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
/** 我们的存储 domain:唯一身份/版本/schema 来源。 */
|
|
38
|
+
export const CATALOG_DOMAIN = defineDomain({
|
|
39
|
+
name: 'agentic_tool_catalog',
|
|
40
|
+
version: 1,
|
|
41
|
+
tables: {
|
|
42
|
+
catalogs: domainTable(catalogRecord),
|
|
43
|
+
state: domainTable(lastGoodRecord),
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 创建目录存储。
|
|
49
|
+
* @param ctx - 插件 ctx(需要 `ctx.get('storageDomain')` 与 effect 生命周期)。
|
|
50
|
+
* @param shared - 进程级共享状态(`groups` Map / `lastGood`)。
|
|
51
|
+
* @param logger - 可选 logger。
|
|
52
|
+
*/
|
|
53
|
+
export function createCatalogStore(ctx, shared, logger) {
|
|
54
|
+
const storageDomain = ctx.get?.('storageDomain')
|
|
55
|
+
const info = { available: false, state: storageDomain === undefined ? 'unavailable' : 'opening', lastError: null }
|
|
56
|
+
let domain
|
|
57
|
+
let ready = Promise.resolve(false)
|
|
58
|
+
|
|
59
|
+
const log = (message) => logger?.warn?.(`dsh-agentic-tool-catalog: ${message}`)
|
|
60
|
+
|
|
61
|
+
if (storageDomain !== undefined) {
|
|
62
|
+
ready = (async () => {
|
|
63
|
+
try {
|
|
64
|
+
domain = await storageDomain.open(CATALOG_DOMAIN)
|
|
65
|
+
ctx.effect(() => () => domain.close())
|
|
66
|
+
for (const [fingerprint, record] of domain.table('catalogs').entries()) {
|
|
67
|
+
if (record !== undefined && Array.isArray(record.groups)) shared.groups.set(fingerprint, record.groups)
|
|
68
|
+
}
|
|
69
|
+
const lastGood = domain.table('state').get('lastGood')
|
|
70
|
+
if (lastGood !== undefined && Array.isArray(lastGood.groups)) shared.lastGood = lastGood
|
|
71
|
+
info.available = true
|
|
72
|
+
info.state = 'ready'
|
|
73
|
+
return true
|
|
74
|
+
} catch (error) {
|
|
75
|
+
info.available = false
|
|
76
|
+
info.state = 'failed'
|
|
77
|
+
info.lastError = String(error?.message ?? error).slice(0, 200)
|
|
78
|
+
log(`官方 Storage 打开失败,使用纯内存缓存:${info.lastError}`)
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
})()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** 启动时 hydrate 完成(true=官方存储可用;false=内存模式)。 */
|
|
85
|
+
const whenReady = () => ready
|
|
86
|
+
const available = () => info.available
|
|
87
|
+
const status = () => ({ ...info })
|
|
88
|
+
|
|
89
|
+
/** 保存一版目录分组(fire-and-forget;失败只记日志)。 */
|
|
90
|
+
const saveGroups = (fingerprint, groups) => {
|
|
91
|
+
shared.groups.set(fingerprint, groups)
|
|
92
|
+
if (!info.available) return
|
|
93
|
+
Promise.resolve()
|
|
94
|
+
.then(() => domain.table('catalogs').put(fingerprint, { fingerprint, groups, createdAt: Date.now() }))
|
|
95
|
+
.catch((error) => log(`目录缓存写入失败(不影响当前会话):${String(error?.message ?? error).slice(0, 160)}`))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** 保存最近一次成功分组(fire-and-forget)。 */
|
|
99
|
+
const saveLastGood = (lastGood) => {
|
|
100
|
+
shared.lastGood = lastGood
|
|
101
|
+
if (!info.available) return
|
|
102
|
+
Promise.resolve()
|
|
103
|
+
.then(() => domain.table('state').put('lastGood', lastGood))
|
|
104
|
+
.catch((error) => log(`最近成功分组写入失败(不影响当前会话):${String(error?.message ?? error).slice(0, 160)}`))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { whenReady, available, status, saveGroups, saveLastGood, info }
|
|
108
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* subagent-compat.js —— 唯一知道宿主 `ctx.subagents.startActivation()` 入参形状的地方。
|
|
3
|
+
*
|
|
4
|
+
* 背景(2026-11-12 实测):
|
|
5
|
+
* - DSH 0.2.1-rc.2 … 0.2.2-rc.1:请求字段装在 `spec.request` 里
|
|
6
|
+
* (`{ provider, label, request: { parent, prompt, maxDepth, outputSchema, ... }, signal, delivery }`);
|
|
7
|
+
* - DSH 0.2.2-rc.2+:同一组字段被拍平到 spec 顶层,`startActivation` 直接读 `spec.parent.id`。
|
|
8
|
+
*
|
|
9
|
+
* 兼容策略:同时发送两种形状。
|
|
10
|
+
* - 顶层字段服务 0.2.2-rc.2+;
|
|
11
|
+
* - 不可枚举的 `request` 属性服务旧版(旧版只做属性读取 `spec.request`,仍可访问);
|
|
12
|
+
* - 不可枚举很重要:rc.2 的外部 provider 路径会把 spec 的剩余字段展开进 provider 请求,
|
|
13
|
+
* 可枚举的 `request` 会泄漏过去;不可枚举属性不会被展开。
|
|
14
|
+
* - 两个版本的实现都只读自己认识的字段、不校验未知字段,因此并集是安全的。
|
|
15
|
+
*
|
|
16
|
+
* 这里是**唯一**允许出现版本形状差异的文件;其余代码只调用 `startSubagentActivation()`。
|
|
17
|
+
* 契约由 `scripts/verify-subagent-compat.mjs` 用真实的
|
|
18
|
+
* `@deepseek-ai/dsh-subagent@0.2.2-rc.1` / `@0.2.2-rc.2` 实现验证。
|
|
19
|
+
*
|
|
20
|
+
* @module @local/dsh-agentic-tool-catalog/subagent-compat
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** 适配器同时支持的激活 spec 形状(用于状态观测;不代表宿主版本)。 */
|
|
24
|
+
export const ACTIVATION_SHAPES = Object.freeze(['flat', 'nested'])
|
|
25
|
+
|
|
26
|
+
/** 属于 `SubagentStartRequest`(随版本在顶层 / request 之间搬家的字段)。 */
|
|
27
|
+
export const ACTIVATION_REQUEST_FIELDS = Object.freeze([
|
|
28
|
+
'cwd',
|
|
29
|
+
'agentPreset',
|
|
30
|
+
'prompt',
|
|
31
|
+
'parent',
|
|
32
|
+
'agentOptions',
|
|
33
|
+
'outputSchema',
|
|
34
|
+
'maxDepth',
|
|
35
|
+
])
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 把扁平请求构造成两种宿主都能读的 activation spec。
|
|
39
|
+
* @param spec - 扁平请求(含 provider / label / signal / delivery 与 request 字段)。
|
|
40
|
+
* @returns 可直接传给 `ctx.subagents.startActivation()` 的 spec。
|
|
41
|
+
*/
|
|
42
|
+
export function buildSubagentActivationSpec(spec) {
|
|
43
|
+
const request = {}
|
|
44
|
+
for (const key of ACTIVATION_REQUEST_FIELDS) {
|
|
45
|
+
if (spec[key] !== undefined) request[key] = spec[key]
|
|
46
|
+
}
|
|
47
|
+
const activationSpec = {
|
|
48
|
+
provider: spec.provider,
|
|
49
|
+
label: spec.label,
|
|
50
|
+
...(spec.name === undefined ? {} : { name: spec.name }),
|
|
51
|
+
...(spec.childId === undefined ? {} : { childId: spec.childId }),
|
|
52
|
+
// 0.2.2-rc.2+:字段在顶层。
|
|
53
|
+
...request,
|
|
54
|
+
signal: spec.signal,
|
|
55
|
+
delivery: spec.delivery,
|
|
56
|
+
}
|
|
57
|
+
// 0.2.1-rc.2 … 0.2.2-rc.1:字段在 request 里。不可枚举,避免 rc.2 的外部 provider 展开时泄漏。
|
|
58
|
+
Object.defineProperty(activationSpec, 'request', {
|
|
59
|
+
value: request,
|
|
60
|
+
enumerable: false,
|
|
61
|
+
configurable: true,
|
|
62
|
+
writable: false,
|
|
63
|
+
})
|
|
64
|
+
return activationSpec
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 启动一个子代理激活,自动适配宿主的 spec 形状。
|
|
69
|
+
* @param subagents - `ctx.get('subagents')` 得到的服务。
|
|
70
|
+
* @param spec - 扁平请求(见 {@link ACTIVATION_REQUEST_FIELDS})。
|
|
71
|
+
* @returns `startActivation()` 的原样结果。
|
|
72
|
+
*/
|
|
73
|
+
export function startSubagentActivation(subagents, spec) {
|
|
74
|
+
return subagents.startActivation(buildSubagentActivationSpec(spec))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 判断一个失败是否像"宿主不认识 activation spec 形状"(而非业务/模型失败)。
|
|
79
|
+
* 仅用于观测与软闸门:形状错误发生在创建子代理之前,无副作用。
|
|
80
|
+
* @param error - 捕获到的错误。
|
|
81
|
+
* @returns 是否匹配各版本读取缺失字段时的 TypeError。
|
|
82
|
+
*/
|
|
83
|
+
export function isActivationShapeError(error) {
|
|
84
|
+
if (!(error instanceof TypeError)) return false
|
|
85
|
+
const message = String(error.message ?? '')
|
|
86
|
+
return /Cannot read properties of undefined \(reading '(?:id|parent|prompt|request|maxDepth|outputSchema|cwd|agentPreset|agentOptions)'\)/.test(message)
|
|
87
|
+
}
|
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,92 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agentic-tool-catalog",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.3.2",
|
|
4
|
+
"description": "Agentic Tool Catalog for DSH: an agent builds and maintains a tool catalog; schemas stay out of context and tools load on demand.",
|
|
5
|
+
"icon": "./icon.svg",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./lib/index.js",
|
|
9
|
+
"./client": "./lib/client.js",
|
|
10
|
+
"./package.json": "./package.json",
|
|
11
|
+
"./locale/*.json": "./locale/*.json"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"CHANGELOG.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"README.md",
|
|
17
|
+
"README.zh.md",
|
|
18
|
+
"cordis.patch.yml",
|
|
19
|
+
"docs/assets/*",
|
|
20
|
+
"docs/assets/plugin-page.png",
|
|
21
|
+
"docs/benchmark.md",
|
|
22
|
+
"docs/data-card.md",
|
|
23
|
+
"docs/detail-benchmark.svg",
|
|
24
|
+
"docs/detail-hero.svg",
|
|
25
|
+
"docs/detail.md",
|
|
26
|
+
"docs/detail.zh.md",
|
|
27
|
+
"icon.svg",
|
|
28
|
+
"lib/*.js",
|
|
29
|
+
"locale/*.json"
|
|
30
|
+
],
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@deepseek-ai/dsh-scope": "^0.2.0-rc.2",
|
|
33
|
+
"@deepseek-ai/dsh-tools": "^0.2.0-rc.2",
|
|
34
|
+
"@deepseek-ai/schemastery": "~3.18.4",
|
|
35
|
+
"@deepseek-ai/dsh-storage-domain": "^0.2.2-rc.1",
|
|
36
|
+
"zod": "^4.4.3"
|
|
37
|
+
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"@deepseek-ai/dsh-scope": {
|
|
40
|
+
"optional": true
|
|
41
|
+
},
|
|
42
|
+
"@deepseek-ai/dsh-tools": {
|
|
43
|
+
"optional": true
|
|
44
|
+
},
|
|
45
|
+
"@deepseek-ai/schemastery": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"dsh": {
|
|
50
|
+
"bundle": {
|
|
51
|
+
"patch": "./cordis.patch.yml"
|
|
52
|
+
},
|
|
53
|
+
"client": {
|
|
54
|
+
"platform": "web",
|
|
55
|
+
"immediately": true,
|
|
56
|
+
"inject": [
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-conversation"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"repository": {
|
|
63
|
+
"type": "git",
|
|
64
|
+
"url": "git+https://github.com/laughinbytes/dsh-agentic-tool-catalog.git"
|
|
65
|
+
},
|
|
66
|
+
"keywords": [
|
|
67
|
+
"dsh",
|
|
68
|
+
"dsh-plugin",
|
|
69
|
+
"deepseek-harness",
|
|
70
|
+
"tool-catalog",
|
|
71
|
+
"on-demand",
|
|
72
|
+
"mcp",
|
|
73
|
+
"context",
|
|
74
|
+
"agent"
|
|
75
|
+
],
|
|
76
|
+
"author": "laughinbytes",
|
|
77
|
+
"homepage": "https://github.com/laughinbytes/dsh-agentic-tool-catalog#readme",
|
|
78
|
+
"bugs": {
|
|
79
|
+
"url": "https://github.com/laughinbytes/dsh-agentic-tool-catalog/issues"
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=22.19.0",
|
|
83
|
+
"dsh": ">=0.2.1-rc.2 <0.3.0-0"
|
|
84
|
+
},
|
|
85
|
+
"scripts": {
|
|
86
|
+
"test": "node scripts/verify-agentic-tool-catalog.mjs && node scripts/verify-agentic-tool-catalog-schema.mjs && node scripts/verify-subagent-compat.mjs",
|
|
87
|
+
"test:schema": "node scripts/verify-agentic-tool-catalog-schema.mjs",
|
|
88
|
+
"test:compat": "node scripts/verify-subagent-compat.mjs",
|
|
89
|
+
"pack:check": "npm pack --dry-run",
|
|
90
|
+
"test:presentation": "node scripts/verify-presentation.mjs"
|
|
91
|
+
}
|
|
92
|
+
}
|