@nanmicoder/dsh-agent-teams 0.1.15-alpha.1 → 0.1.16-rc.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanmicoder/dsh-agent-teams",
3
- "version": "0.1.15-alpha.1",
3
+ "version": "0.1.16-rc.1",
4
4
  "description": "AgentTeams for DeepSeek Harness: multi-agent team collaboration (captain, members, tasks with dependencies, messaging) driven by natural language, with a tree monitor in the web GUI",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -25,7 +25,10 @@
25
25
  "cordis.patch.yml",
26
26
  "release-notes",
27
27
  "README.md",
28
- "README_ZH.md"
28
+ "README_ZH.md",
29
+ "compatibility.json",
30
+ "scripts/doctor.mjs",
31
+ "scripts/compatibility.mjs"
29
32
  ],
30
33
  "license": "MIT",
31
34
  "author": {
@@ -56,7 +59,7 @@
56
59
  },
57
60
  "publishConfig": {
58
61
  "access": "public",
59
- "tag": "alpha",
62
+ "tag": "next",
60
63
  "registry": "https://registry.npmjs.org/"
61
64
  },
62
65
  "dsh": {
@@ -76,39 +79,29 @@
76
79
  "platform": "web"
77
80
  }
78
81
  },
79
- "scripts": {
80
- "build": "node scripts/clean-build.mjs && tsc -p tsconfig.json && tsc -p tsconfig.client.json && tsdown",
81
- "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
82
- "sync:skill": "node scripts/sync-skill.mjs",
83
- "verify:skill": "node scripts/sync-skill.mjs --check",
84
- "verify": "node scripts/verify.mjs && node scripts/fallback-tdd.mjs && node scripts/quality-gates-tdd.mjs && node scripts/lifecycle-verify.mjs && node scripts/stress-verify.mjs && pnpm verify:web-routes && pnpm verify:release && pnpm verify:skill",
85
- "prepublishOnly": "pnpm build && pnpm verify",
86
- "verify:web-routes": "node scripts/web-routes-verify.mjs",
87
- "verify:release": "node --test scripts/release-metadata.test.mjs"
88
- },
89
82
  "peerDependencies": {
90
83
  "@deepseek-ai/cordis": "^4.0.2",
91
- "@deepseek-ai/dsh-agent": "^0.1.2-alpha.2",
92
- "@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.2",
93
- "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.2",
94
- "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.2",
95
- "@deepseek-ai/dsh-client-store": "^0.1.2-alpha.2",
96
- "@deepseek-ai/dsh-client-ui-chat": "^0.1.2-alpha.2",
97
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.2-alpha.2",
98
- "@deepseek-ai/dsh-client-ui-layout": "^0.1.2-alpha.2",
99
- "@deepseek-ai/dsh-client-ui-model-selection": "^0.1.2-alpha.2",
100
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-alpha.2",
101
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.2",
102
- "@deepseek-ai/dsh-client-ui-session": "^0.1.2-alpha.2",
103
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.2",
104
- "@deepseek-ai/dsh-commands": "^0.1.2-alpha.2",
105
- "@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
106
- "@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
107
- "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.2",
108
- "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.2",
109
- "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.2",
110
- "@deepseek-ai/dsh-tools": "^0.1.2-alpha.2",
111
- "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.2",
84
+ "@deepseek-ai/dsh-agent": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
85
+ "@deepseek-ai/dsh-api-session-controller": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
86
+ "@deepseek-ai/dsh-client-connection": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
87
+ "@deepseek-ai/dsh-client-locale": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
88
+ "@deepseek-ai/dsh-client-store": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
89
+ "@deepseek-ai/dsh-client-ui-chat": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
90
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
91
+ "@deepseek-ai/dsh-client-ui-layout": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
92
+ "@deepseek-ai/dsh-client-ui-model-selection": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
93
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
94
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
95
+ "@deepseek-ai/dsh-client-ui-session": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
96
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
97
+ "@deepseek-ai/dsh-commands": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
98
+ "@deepseek-ai/dsh-llm": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
99
+ "@deepseek-ai/dsh-session": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
100
+ "@deepseek-ai/dsh-session-projection": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
101
+ "@deepseek-ai/dsh-subagent": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
102
+ "@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
103
+ "@deepseek-ai/dsh-tools": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
104
+ "@deepseek-ai/dsh-util-values": "0.1.2-rc.1 || 0.1.2-alpha.5 || 0.1.2-alpha.2",
112
105
  "@deepseek-ai/schemastery": "^3.18.2",
113
106
  "react": "^18.2.0"
114
107
  },
@@ -188,29 +181,30 @@
188
181
  },
189
182
  "devDependencies": {
190
183
  "@deepseek-ai/cordis": "^4.0.2",
191
- "@deepseek-ai/dsh": "0.1.2-alpha.2",
192
- "@deepseek-ai/dsh-agent": "0.1.2-alpha.2",
193
- "@deepseek-ai/dsh-api-session-controller": "0.1.2-alpha.2",
194
- "@deepseek-ai/dsh-client-connection": "0.1.2-alpha.2",
195
- "@deepseek-ai/dsh-client-locale": "0.1.2-alpha.2",
196
- "@deepseek-ai/dsh-client-store": "0.1.2-alpha.2",
197
- "@deepseek-ai/dsh-client-ui-chat": "0.1.2-alpha.2",
198
- "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-alpha.2",
199
- "@deepseek-ai/dsh-client-ui-layout": "0.1.2-alpha.2",
200
- "@deepseek-ai/dsh-client-ui-model-selection": "0.1.2-alpha.2",
201
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-alpha.2",
202
- "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-alpha.2",
203
- "@deepseek-ai/dsh-client-ui-session": "0.1.2-alpha.2",
204
- "@deepseek-ai/dsh-client-ui-slots": "0.1.2-alpha.2",
205
- "@deepseek-ai/dsh-commands": "0.1.2-alpha.2",
206
- "@deepseek-ai/dsh-llm": "0.1.2-alpha.2",
207
- "@deepseek-ai/dsh-session": "0.1.2-alpha.2",
208
- "@deepseek-ai/dsh-session-projection": "0.1.2-alpha.2",
209
- "@deepseek-ai/dsh-subagent": "0.1.2-alpha.2",
210
- "@deepseek-ai/dsh-system-prompt": "0.1.2-alpha.2",
211
- "@deepseek-ai/dsh-tools": "0.1.2-alpha.2",
212
- "@deepseek-ai/dsh-util-values": "0.1.2-alpha.2",
213
- "@deepseek-ai/dsh-workspace": "0.1.2-alpha.2",
184
+ "@deepseek-ai/dsh": "0.1.2-rc.1",
185
+ "@deepseek-ai/dsh-agent": "0.1.2-rc.1",
186
+ "@deepseek-ai/dsh-api-session-controller": "0.1.2-rc.1",
187
+ "@deepseek-ai/dsh-client-connection": "0.1.2-rc.1",
188
+ "@deepseek-ai/dsh-client-locale": "0.1.2-rc.1",
189
+ "@deepseek-ai/dsh-client-store": "0.1.2-rc.1",
190
+ "@deepseek-ai/dsh-client-ui-chat": "0.1.2-rc.1",
191
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1",
192
+ "@deepseek-ai/dsh-client-ui-layout": "0.1.2-rc.1",
193
+ "@deepseek-ai/dsh-client-ui-model-selection": "0.1.2-rc.1",
194
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
195
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1",
196
+ "@deepseek-ai/dsh-client-ui-session": "0.1.2-rc.1",
197
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
198
+ "@deepseek-ai/dsh-commands": "0.1.2-rc.1",
199
+ "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
200
+ "@deepseek-ai/dsh-llm-retry": "0.1.2-rc.1",
201
+ "@deepseek-ai/dsh-session": "0.1.2-rc.1",
202
+ "@deepseek-ai/dsh-session-projection": "0.1.2-rc.1",
203
+ "@deepseek-ai/dsh-subagent": "0.1.2-rc.1",
204
+ "@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1",
205
+ "@deepseek-ai/dsh-tools": "0.1.2-rc.1",
206
+ "@deepseek-ai/dsh-util-values": "0.1.2-rc.1",
207
+ "@deepseek-ai/dsh-workspace": "0.1.2-rc.1",
214
208
  "@deepseek-ai/schemastery": "^3.18.2",
215
209
  "@types/node": "^24.13.3",
216
210
  "@types/react": "~18.3.1",
@@ -220,5 +214,21 @@
220
214
  "react-dom": "^18.2.0",
221
215
  "tsdown": "0.22.2",
222
216
  "typescript": "^5.9.3"
217
+ },
218
+ "bin": {
219
+ "dsh-agent-teams-doctor": "scripts/doctor.mjs"
220
+ },
221
+ "scripts": {
222
+ "build": "node scripts/clean-build.mjs && tsc -p tsconfig.json && tsc -p tsconfig.client.json && tsdown",
223
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
224
+ "sync:skill": "node scripts/sync-skill.mjs",
225
+ "verify:skill": "node scripts/sync-skill.mjs --check",
226
+ "verify": "node scripts/verify.mjs && node scripts/fallback-tdd.mjs && node scripts/member-failure-tdd.mjs && node scripts/quality-gates-tdd.mjs && node scripts/lifecycle-verify.mjs && node scripts/stress-verify.mjs && pnpm verify:web-routes && pnpm verify:release && pnpm verify:skill && pnpm verify:harness-contract && pnpm verify:http-body && pnpm verify:compatibility",
227
+ "verify:web-routes": "node scripts/web-routes-verify.mjs",
228
+ "verify:release": "node --test scripts/release-metadata.test.mjs",
229
+ "verify:harness-contract": "node scripts/harness-compat-tdd.mjs && node scripts/lifecycle-verify.mjs --modern-harness && node scripts/member-failure-tdd.mjs --modern-harness",
230
+ "verify:compatibility": "node scripts/compatibility.mjs && node --test scripts/compatibility.test.mjs && node scripts/doctor.mjs",
231
+ "verify:http-body": "node --test scripts/http-body.test.mjs",
232
+ "doctor": "node scripts/doctor.mjs"
223
233
  }
224
- }
234
+ }
@@ -7,7 +7,7 @@ This release restores AgentTeams activity and member history after a DeepSeek Ha
7
7
  - **Cold-start activity recovery**: selecting a captain session now performs a lightweight live and archived team discovery pass, so older sessions without an AgentTeams conversation card restore their activity panel after restart.
8
8
  - **Reliable member activity**: live `running / idle / ready` state is resolved from the durable team roster and Agent registry instead of depending on the versioned `listChildren()` projection shape.
9
9
  - **Persistent member history**: retired AgentTeams members remain discoverable in the Harness subagent catalog for transcript review while direct follow-up remains blocked.
10
- - **rc.8 transcript navigation**: activity-panel and conversation-card member links refresh the parent catalog and use addressed `openSubagent()` navigation, with a fallback for older Harness runtimes.
10
+ - **rc.8 transcript navigation**: activity-panel and conversation-card member links refresh the parent catalog and use `openSubagent()` navigation with parent-child addressing, with a fallback for older Harness runtimes.
11
11
 
12
12
  ## Verification
13
13
 
@@ -0,0 +1,57 @@
1
+ # AgentTeams v0.1.15
2
+
3
+ **The default install now supports DeepSeek Harness 0.1.2-alpha.2.**
4
+ This release publishes to npm **latest** and is the latest GitHub release.
5
+ It replaces the opt-in-only release policy of 0.1.15-alpha.1, with no plugin
6
+ runtime changes.
7
+
8
+ ## Install / upgrade
9
+
10
+ Use the current supported Harness developer preview and the latest plugin:
11
+
12
+ ```sh
13
+ npm install --global @deepseek-ai/dsh@0.1.2-alpha.2
14
+ dsh --version
15
+ dsh plugin --profile web add @nanmicoder/dsh-agent-teams@latest
16
+ ```
17
+
18
+ Use `@0.1.15` instead of `@latest` to pin this release. Restart Harness and
19
+ refresh the browser after updating. The host's Alpha.2 version does not
20
+ require a separate Alpha plugin channel.
21
+
22
+ ## Older hosts
23
+
24
+ If Harness must stay on **0.1.0-rc.8**, install the historical plugin explicitly:
25
+
26
+ ```sh
27
+ dsh plugin --profile web add @nanmicoder/dsh-agent-teams@0.1.14
28
+ ```
29
+
30
+ Other older hosts should likewise pin their known-working plugin version;
31
+ they must not follow `@latest`. There is no dual-version adapter. Updating
32
+ the plugin does not update the running host, including a source-built host.
33
+ See the README for source-build and rollback instructions.
34
+
35
+ ## Included fixes and validation
36
+
37
+ - Alpha.2 Web client activation using current conversation, session and store APIs.
38
+ - Host authentication and Host/Origin checks on raw state, plan, halt and artwork routes.
39
+ - The same runtime previously exercised with real DeepSeek API calls, three-member
40
+ order-settlement delivery, and human UI review in `/tmp`.
41
+ - Rebuild, full automated verification and runtime-payload comparison for this
42
+ release. This publication-policy change does not claim a second full business run.
43
+
44
+ See the [real API / UI acceptance record](https://github.com/NanmiCoder/dsh-agent-teams/blob/v0.1.15/docs/alpha2-release-acceptance.md)
45
+ for the recovered model mistakes, screenshots methodology and environment limits.
46
+
47
+ ## 中文说明
48
+
49
+ **默认最新插件 0.1.15 适配 Harness 0.1.2-alpha.2,发布到 `latest`。**
50
+ Harness 本身处于开发者预览阶段,插件默认发行跟进当前已适配的新宿主;
51
+ 不再因为宿主带 Alpha 名称,就把适配放到必须主动选择的插件 Alpha 渠道。
52
+
53
+ 新宿主使用插件 `@latest`(当前为 0.1.15);旧宿主应安装明确兼容的固定版本,
54
+ 例如 Harness 0.1.0-rc.8 使用插件 `@0.1.14`。没有更新 Harness 源码或安装版本的用户,
55
+ 不要只把插件更新到 latest。中英文 README 已给出完整操作方法。
56
+
57
+ 本次调整版本、发行渠道和文档,运行时代码与此前真实业务 / UI 验收的 Alpha.2 适配一致。
@@ -0,0 +1,33 @@
1
+ # AgentTeams v0.1.16-rc.1 — release candidate
2
+
3
+ This candidate restores team execution across the exact Harness targets `0.1.2-rc.1`, `0.1.2-alpha.5`, and `0.1.2-alpha.2`. Its release channel is npm `next`; install the exact plugin version with `dsh plugin --profile web add --save-exact @nanmicoder/dsh-agent-teams@0.1.16-rc.1` after checking that the running host is one of those targets. This is a prerelease, and older Desktop cores require their own compatible pair.
4
+
5
+ ## Compatibility and correctness
6
+
7
+ - Centralize old and new host contracts: receiver-bound legacy setup, synchronous modern session setup, own session events, FIFO continuation, and retired-member delivery guards. Preserve model selection, reasoning effort, fallback, failure notification, and cleanup on both host generations.
8
+ - Persist fallback settings when adding members manually; restore the active fallback route after a cold restart. Real-host testing exposed the missing persisted setting even when mocked tests passed.
9
+ - Require the captain to use safe reassignment instead of claiming tasks on behalf of members. Preserve idempotent reads of an already active captain takeover.
10
+ - Align the status tool and captain instructions around member notifications, removing the instruction to repeatedly poll for progress.
11
+ - Bound both plan and halt JSON requests by bytes, reject malformed/non-object bodies, and keep the host Connection authentication gate.
12
+
13
+ ## Contributions
14
+
15
+ The host adapter incorporates the migration analysis and reproduction cases from [Adol1111's #119](https://github.com/NanmiCoder/dsh-agent-teams/pull/119), [Zn-Dk's #124](https://github.com/NanmiCoder/dsh-agent-teams/pull/124), and [chenkai2's #130](https://github.com/NanmiCoder/dsh-agent-teams/pull/130). Their proposals were integrated at one compatibility boundary, with additional receiver, lifecycle, FIFO and failure coverage.
16
+
17
+ Separately merged contributions retain their original authors and commits: [#26](https://github.com/NanmiCoder/dsh-agent-teams/pull/26), [#63](https://github.com/NanmiCoder/dsh-agent-teams/pull/63), [#74](https://github.com/NanmiCoder/dsh-agent-teams/pull/74), [#82](https://github.com/NanmiCoder/dsh-agent-teams/pull/82), [#83](https://github.com/NanmiCoder/dsh-agent-teams/pull/83), [#84](https://github.com/NanmiCoder/dsh-agent-teams/pull/84), [#86](https://github.com/NanmiCoder/dsh-agent-teams/pull/86), [#90](https://github.com/NanmiCoder/dsh-agent-teams/pull/90), and [#93](https://github.com/NanmiCoder/dsh-agent-teams/pull/93). These cover argument validation, wording, parked-attempt recovery, panel resizing, working indicators, model badges, Windows build paths, and client service declarations.
18
+
19
+ ## Maintenance and release policy
20
+
21
+ - Use `compatibility.json` for exact support targets, development dependency validation and the CI matrix. Freeze the complete DSH development dependency cohort; an exact CLI version alone does not freeze its transitive packages.
22
+ - Add a read-only installation doctor for version mixing, missing packages, duplicate runtime identities and profile/plugin mismatches.
23
+ - Vendor the community upgrade, audit, benchmark, testing and release skills with a pinned source revision and license. Keep local rules separate from upstream guidance.
24
+ - Require Ubuntu and Windows static checks plus real packaged-plugin execution on all three hosts before publication. Every host verifies the same tgz, and the publish job verifies its digest before publishing that artifact.
25
+ - Use `next` for plugin prereleases. A stable `latest` requires the recommended host and complete matrix; Alpha-only validation is insufficient.
26
+
27
+ ## Verification scope
28
+
29
+ The runtime runner installs actual npm host packages and the candidate artifact in isolated directories, then exercises the CLI, profile, plugin tools, member sessions, messages, reasoning, fallback, failure handling and cold restoration. The external model adapter supplies deterministic responses so the suite can run without credentials. It is not a real-provider benchmark or a claim about every Desktop/OS/version combination.
30
+
31
+ Separate real DeepSeek API acceptance using `deepseek-v4-flash` passed on all three hosts from `/tmp`, plus a real-model rc.1 Web workflow. Ubuntu/Windows typecheck, build and full verify, and the three Ubuntu host cohorts passed in CI. These checks do not establish native Desktop compatibility or resolve the independent OOM investigation.
32
+
33
+ See the repository's [maintenance workflow](../docs/maintenance-workflow.md) and the dated implementation/acceptance record for the exact outcomes and remaining limits. Older `0.1.0-*` and `0.1.1-*` hosts are outside this candidate's support matrix; retain a known compatible exact pair or migrate both sides.
@@ -0,0 +1,90 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { pathToFileURL } from 'node:url'
3
+
4
+ export const policy = JSON.parse(readFileSync(new URL('../compatibility.json', import.meta.url), 'utf8'))
5
+
6
+ // Contract consumed by the published server entry and client module table.
7
+ // Keeping this independent from package.json catches accidentally deleted peers.
8
+ export const requiredHostPeers = [
9
+ '@deepseek-ai/cordis', '@deepseek-ai/schemastery', 'react',
10
+ ...[
11
+ 'agent', 'api-session-controller', 'client-connection', 'client-locale',
12
+ 'client-store', 'client-ui-chat', 'client-ui-conversation', 'client-ui-layout',
13
+ 'client-ui-model-selection', 'client-ui-primitives', 'client-ui-renderer',
14
+ 'client-ui-session', 'client-ui-slots', 'commands', 'llm', 'session',
15
+ 'session-projection', 'subagent', 'system-prompt', 'tools', 'util-values',
16
+ ].map(name => `@deepseek-ai/dsh-${name}`),
17
+ ]
18
+
19
+ // These are value imports of the server entry, so optional-peer metadata does
20
+ // not make an unresolved installation executable. Client services can instead
21
+ // be supplied by the host's browser module table.
22
+ export const serverRuntimePeers = [
23
+ '@deepseek-ai/schemastery', '@deepseek-ai/dsh-agent', '@deepseek-ai/dsh-llm',
24
+ '@deepseek-ai/dsh-session', '@deepseek-ai/dsh-subagent', '@deepseek-ai/dsh-tools',
25
+ ]
26
+
27
+ export function validatePolicy(input) {
28
+ if (input.schemaVersion !== 1 || input.previewTag !== 'next') throw new Error('Unsupported compatibility policy')
29
+ const hosts = input.supportedHosts
30
+ if (!Array.isArray(hosts) || hosts.length === 0) throw new Error('supportedHosts must list exact tested host targets')
31
+ const versions = new Set()
32
+ for (const host of hosts) {
33
+ if (typeof host.version !== 'string' || !/^\d+\.\d+\.\d+(?:-(?:alpha|beta|rc)\.\d+)?$/.test(host.version)
34
+ || host.version.trim() !== host.version || versions.has(host.version)
35
+ || !['recommended', 'preview', 'legacy'].includes(host.track)) {
36
+ throw new Error('Host targets must be unique exact versions with an explicit track')
37
+ }
38
+ versions.add(host.version)
39
+ }
40
+ const recommended = hosts.filter(host => host.track === 'recommended')
41
+ if (recommended.length !== 1 || recommended[0].version !== input.recommendedHost) throw new Error('Exactly one recommended host is required')
42
+ if (/-(alpha|beta)\./.test(input.recommendedHost)) throw new Error('Alpha/beta hosts cannot be the ordinary-user recommendation')
43
+ return hosts.map(host => host.version)
44
+ }
45
+
46
+ export function validatePackageCompatibility(pkg, input = policy) {
47
+ const versions = validatePolicy(input)
48
+ for (const name of requiredHostPeers) {
49
+ if (typeof pkg.peerDependencies?.[name] !== 'string' || pkg.peerDependencies[name].trim() === '') {
50
+ throw new Error(`Missing required host peer declaration: ${name}`)
51
+ }
52
+ }
53
+ const baseline = pkg.devDependencies?.['@deepseek-ai/dsh']
54
+ if (!versions.includes(baseline)) throw new Error('Development host must be an exact supported target')
55
+ for (const [name, version] of Object.entries(pkg.devDependencies ?? {})) {
56
+ if (name === '@deepseek-ai/dsh' || name.startsWith('@deepseek-ai/dsh-')) {
57
+ if (version !== baseline) throw new Error(`${name} must match the exact development host ${baseline}`)
58
+ }
59
+ }
60
+ const overrides = pkg.pnpm?.overrides ?? {}
61
+ const dshNames = Object.keys(pkg.devDependencies ?? {}).filter(name => name === '@deepseek-ai/dsh' || name.startsWith('@deepseek-ai/dsh-'))
62
+ for (const name of dshNames) {
63
+ if (overrides[name] !== baseline) throw new Error(`${name} requires an exact pnpm override to ${baseline}`)
64
+ }
65
+ for (const [name, version] of Object.entries(overrides)) {
66
+ if (name.includes('@deepseek-ai/dsh')) {
67
+ if (!/^@deepseek-ai\/dsh(?:-[a-z0-9-]+)?$/.test(name)) {
68
+ throw new Error(`${name} override must use a bare DSH package name, without ranges or parent selectors`)
69
+ }
70
+ if (version !== baseline) throw new Error(`${name} override must match the development host ${baseline}`)
71
+ }
72
+ }
73
+ for (const [name, range] of Object.entries(pkg.peerDependencies ?? {})) {
74
+ if (name === '@deepseek-ai/dsh' || name.startsWith('@deepseek-ai/dsh-')) {
75
+ const declared = range.split('||').map(value => value.trim())
76
+ if (declared.length !== versions.length || versions.some(version => !declared.includes(version))) {
77
+ throw new Error(`${name} peer range must enumerate the supported host targets`)
78
+ }
79
+ }
80
+ }
81
+ return versions
82
+ }
83
+
84
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
85
+ const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
86
+ const hosts = validatePackageCompatibility(pkg)
87
+ if (process.argv[2] === '--github-output') console.log(`hosts=${JSON.stringify(hosts)}`)
88
+ else if (process.argv.length === 2) console.log(`Compatibility targets: ${hosts.join(', ')}`)
89
+ else throw new Error('Usage: node scripts/compatibility.mjs [--github-output]')
90
+ }
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readFileSync, realpathSync } from 'node:fs'
3
+ import { dirname, join, resolve } from 'node:path'
4
+ import { pathToFileURL } from 'node:url'
5
+ import { policy, serverRuntimePeers, validatePolicy } from './compatibility.mjs'
6
+
7
+ function manifest(path) {
8
+ return JSON.parse(readFileSync(path, 'utf8'))
9
+ }
10
+
11
+ function locatePackage(name, from) {
12
+ let directory = resolve(from)
13
+ for (;;) {
14
+ const candidate = join(directory, 'node_modules', name, 'package.json')
15
+ if (existsSync(candidate)) return realpathSync(candidate)
16
+ const parent = dirname(directory)
17
+ if (parent === directory) return undefined
18
+ directory = parent
19
+ }
20
+ }
21
+
22
+ /** Inspect manifests only; never execute packages or read credentials/configuration. */
23
+ export function inspectInstallation(hostRoot, profileRoot) {
24
+ const allowed = validatePolicy(policy)
25
+ const root = resolve(hostRoot)
26
+ const own = join(root, 'package.json')
27
+ const hostPath = existsSync(own) && manifest(own).name === '@deepseek-ai/dsh'
28
+ ? realpathSync(own) : locatePackage('@deepseek-ai/dsh', root)
29
+ if (!hostPath) throw new Error('Cannot locate @deepseek-ai/dsh; pass --host-root for the actual CLI or embedded Desktop package directory')
30
+ const host = manifest(hostPath)
31
+ const visited = new Set()
32
+ const packages = []
33
+ const sharedRuntimes = []
34
+ const missing = []
35
+ function walk(path) {
36
+ path = realpathSync(path)
37
+ if (visited.has(path)) return
38
+ visited.add(path)
39
+ const pkg = manifest(path)
40
+ if (pkg.name === '@deepseek-ai/dsh' || pkg.name?.startsWith('@deepseek-ai/dsh-')) {
41
+ packages.push({ name: pkg.name, version: pkg.version, path: dirname(path) })
42
+ }
43
+ if (pkg.name === '@deepseek-ai/cordis') sharedRuntimes.push({ name: pkg.name, path: dirname(path) })
44
+ for (const name of Object.keys({ ...pkg.dependencies, ...pkg.optionalDependencies, ...pkg.peerDependencies })) {
45
+ const dependency = locatePackage(name, dirname(path))
46
+ if (dependency) walk(dependency)
47
+ else if ((name.startsWith('@deepseek-ai/dsh-') || name === '@deepseek-ai/cordis') && !(name in (pkg.optionalDependencies ?? {}))
48
+ && pkg.peerDependenciesMeta?.[name]?.optional !== true) missing.push(name)
49
+ }
50
+ }
51
+ walk(hostPath)
52
+ let plugin
53
+ if (profileRoot) {
54
+ const path = locatePackage('@nanmicoder/dsh-agent-teams', resolve(profileRoot))
55
+ if (!path) missing.push('@nanmicoder/dsh-agent-teams (profile)')
56
+ else {
57
+ const pkg = manifest(path)
58
+ plugin = { version: pkg.version, path: dirname(path) }
59
+ // Follow normal transitive requirements and optional resolved peers, then
60
+ // check the server's actual value imports even when marked optional.
61
+ walk(path)
62
+ for (const name of serverRuntimePeers) {
63
+ const dependency = locatePackage(name, dirname(path))
64
+ if (dependency) walk(dependency)
65
+ else missing.push(`${name} (plugin runtime)`)
66
+ }
67
+ }
68
+ }
69
+ const mixed = packages.filter(pkg => pkg.version !== host.version)
70
+ const problems = []
71
+ const policyPackage = manifest(new URL('../package.json', import.meta.url))
72
+ if (plugin && plugin.version !== policyPackage.version) {
73
+ problems.push(`Installed plugin ${plugin.version} differs from this policy's plugin ${policyPackage.version}; verify its own host compatibility`)
74
+ }
75
+ if (!allowed.includes(host.version)) problems.push(`Unsupported host ${host.version}; recommended target is ${policy.recommendedHost}`)
76
+ if (mixed.length) problems.push(`${mixed.length} resolved DSH packages differ from host ${host.version}`)
77
+ if (missing.length) problems.push(`Missing packages: ${[...new Set(missing)].join(', ')}`)
78
+ const identities = new Map()
79
+ for (const pkg of [...packages, ...sharedRuntimes]) {
80
+ const paths = identities.get(pkg.name) ?? new Set()
81
+ paths.add(pkg.path)
82
+ identities.set(pkg.name, paths)
83
+ }
84
+ const duplicates = [...identities].filter(([, paths]) => paths.size > 1).map(([name]) => name)
85
+ if (duplicates.length) problems.push(`Multiple resolved identities: ${duplicates.join(', ')}`)
86
+ return {
87
+ ok: problems.length === 0,
88
+ host: { version: host.version, path: dirname(hostPath) },
89
+ ...(plugin ? { plugin } : {}),
90
+ checkedPackages: packages.length, supportedHosts: allowed,
91
+ problems, packages,
92
+ limits: ['Checks files resolved from the supplied roots; does not identify the running process or prove plugin behavior.'],
93
+ }
94
+ }
95
+
96
+ // npm bin entries and macOS temporary directories may reach this module via
97
+ // symlinks; Node canonicalizes import.meta.url but preserves argv[1].
98
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
99
+ try {
100
+ let hostRoot = process.cwd()
101
+ let profileRoot
102
+ let json = false
103
+ for (let index = 2; index < process.argv.length; index++) {
104
+ const arg = process.argv[index]
105
+ if (arg === '--json') json = true
106
+ else if (arg === '--host-root' || arg === '--profile-root') {
107
+ const value = process.argv[++index]
108
+ if (!value || value.startsWith('--')) throw new Error(`${arg} requires a directory`)
109
+ if (arg === '--host-root') hostRoot = value
110
+ else profileRoot = value
111
+ } else throw new Error('Usage: dsh-agent-teams-doctor [--host-root dir] [--profile-root dir] [--json]')
112
+ }
113
+ const result = inspectInstallation(hostRoot, profileRoot)
114
+ console.log(json ? JSON.stringify(result, null, 2) : [
115
+ `Harness: ${result.host.version} (${result.host.path})`,
116
+ `DSH package identities checked: ${result.checkedPackages}`,
117
+ ...result.problems.map(problem => `FAIL: ${problem}`),
118
+ result.ok ? 'Dependency inspection passed.' : 'Use one supported host version and a matching locked profile.',
119
+ ...result.limits,
120
+ ].join('\n'))
121
+ if (!result.ok) process.exitCode = 1
122
+ } catch (error) {
123
+ console.error(error.message)
124
+ process.exitCode = 1
125
+ }
126
+ }