@wowyuarm/dsh-agent-team 0.1.15 → 0.2.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.
Files changed (61) hide show
  1. package/README.md +7 -3
  2. package/README.zh.md +7 -3
  3. package/package.json +49 -47
  4. package/packages/agent-team/lib/context-source.js +11 -6
  5. package/packages/agent-team/lib/environment-check.js +102 -0
  6. package/packages/agent-team/lib/human-profile.js +23 -37
  7. package/packages/agent-team/lib/index.js +28 -2
  8. package/packages/agent-team/lib/installed-manifest.js +32 -0
  9. package/packages/agent-team/lib/ledger.js +35 -0
  10. package/packages/agent-team/lib/member-context.js +2 -4
  11. package/packages/agent-team/lib/member-time-context.js +2 -4
  12. package/packages/agent-team/lib/pressure-policy.js +13 -8
  13. package/packages/agent-team/lib/typert.host.js +121 -44
  14. package/packages/agent-team/lib/typert.remote-client.d.ts +3 -1
  15. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  16. package/packages/agent-team/lib/typert.remote-client.js +86 -38
  17. package/packages/agent-team/lib/types/context-source.d.ts +7 -4
  18. package/packages/agent-team/lib/types/context-source.d.ts.map +1 -1
  19. package/packages/agent-team/lib/types/environment-check.d.ts +66 -0
  20. package/packages/agent-team/lib/types/environment-check.d.ts.map +1 -0
  21. package/packages/agent-team/lib/types/human-profile.d.ts +21 -27
  22. package/packages/agent-team/lib/types/human-profile.d.ts.map +1 -1
  23. package/packages/agent-team/lib/types/index.d.ts +23 -2
  24. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  25. package/packages/agent-team/lib/types/installed-manifest.d.ts +16 -0
  26. package/packages/agent-team/lib/types/installed-manifest.d.ts.map +1 -0
  27. package/packages/agent-team/lib/types/ledger.d.ts +12 -0
  28. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  29. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  30. package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -1
  31. package/packages/agent-team/lib/types/pressure-policy.d.ts +5 -1
  32. package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
  33. package/packages/agent-team/lib/types/types/entities.d.ts +1 -8
  34. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  35. package/packages/agent-team/lib/types/types/requests-results.d.ts +66 -0
  36. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  37. package/packages/client-agent-team/lib/client.js +324 -75
  38. package/packages/client-agent-team/lib/client.js.map +1 -1
  39. package/packages/client-agent-team/lib/types/client/EnvironmentCheck.d.ts +18 -0
  40. package/packages/client-agent-team/lib/types/client/EnvironmentCheck.d.ts.map +1 -0
  41. package/packages/client-agent-team/lib/types/client/EnvironmentCheck.js +58 -0
  42. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts +3 -0
  43. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.d.ts.map +1 -1
  44. package/packages/client-agent-team/lib/types/client/HumanSettingsSection.js +7 -4
  45. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +0 -5
  46. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +1 -1
  47. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
  48. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +2 -7
  49. package/packages/client-agent-team/lib/types/client/environment-check.d.ts +65 -0
  50. package/packages/client-agent-team/lib/types/client/environment-check.d.ts.map +1 -0
  51. package/packages/client-agent-team/lib/types/client/environment-check.js +78 -0
  52. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  53. package/packages/client-agent-team/lib/types/client/index.js +9 -0
  54. package/packages/client-agent-team/lib/types/client/locales.d.ts +18 -8
  55. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  56. package/packages/client-agent-team/lib/types/client/locales.js +21 -8
  57. package/packages/tool-agent-team/lib/context-tools.js +106 -94
  58. package/packages/tool-agent-team/lib/index.js +17 -2
  59. package/packages/tool-agent-team/lib/types/context-tools.d.ts +16 -7
  60. package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
  61. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
package/README.md CHANGED
@@ -41,18 +41,22 @@ If this looks useful, a star on [GitHub](https://github.com/wowyuarm/dsh-agent-t
41
41
 
42
42
  ### 1. Check DSH
43
43
 
44
- This release is certified against DSH `0.1.7-rc.1`. If `dsh` is not installed yet, start DSH with the official package:
44
+ This release is certified against DSH `0.2.0-rc.1`. The current `latest` of `@deepseek-ai/dsh` is `0.1.7-rc.2`, and `0.2.0-rc.1` is published on its `next` tag, so ask for that version explicitly — starting from `latest` skips this bundle on load. On DSH `0.1.7-rc.2`, stay on `@wowyuarm/dsh-agent-team` `0.1.15`.
45
45
 
46
46
  ```sh
47
- npx @deepseek-ai/dsh web
47
+ npx @deepseek-ai/dsh@0.2.0-rc.1 web
48
48
  ```
49
49
 
50
+ Install it globally if you want the `dsh` command for the steps below: `npm i -g @deepseek-ai/dsh@0.2.0-rc.1`.
51
+
50
52
  Stop it, then install Agent Team into the `web` profile:
51
53
 
52
54
  ```sh
53
- dsh plugin --profile web add @wowyuarm/dsh-agent-team
55
+ dsh plugin --profile web add @wowyuarm/dsh-agent-team@0.2.0
54
56
  ```
55
57
 
58
+ The version is pinned deliberately: pnpm skips releases published less than 24 hours ago, so an unpinned `@latest` install resolves to the previous release on release day.
59
+
56
60
  ### 2. Start the Web UI
57
61
 
58
62
  ```sh
package/README.zh.md CHANGED
@@ -41,18 +41,22 @@ Task Thread 把 Claim、Agent 交接、Human 验收和后续回复保留在同
41
41
 
42
42
  ### 1. 检查 DSH
43
43
 
44
- 当前版本已针对 DSH `0.1.7-rc.1` 完成认证。如果还没有安装 `dsh`,先使用官方 package 启动 DSH:
44
+ 当前版本已针对 DSH `0.2.0-rc.1` 完成认证。`@deepseek-ai/dsh` 当前的 `latest` 是 `0.1.7-rc.2`,`0.2.0-rc.1` 发布在它的 `next` 标签上,所以要显式指定这个版本——用 `latest` 启动会在加载时跳过本 bundle。若你的 DSH 是 `0.1.7-rc.2`,请停留在 `@wowyuarm/dsh-agent-team` `0.1.15`。
45
45
 
46
46
  ```sh
47
- npx @deepseek-ai/dsh web
47
+ npx @deepseek-ai/dsh@0.2.0-rc.1 web
48
48
  ```
49
49
 
50
+ 需要下面步骤里的 `dsh` 命令就全局安装:`npm i -g @deepseek-ai/dsh@0.2.0-rc.1`。
51
+
50
52
  先停止它,再把 Agent Team 安装到 `web` profile:
51
53
 
52
54
  ```sh
53
- dsh plugin --profile web add @wowyuarm/dsh-agent-team
55
+ dsh plugin --profile web add @wowyuarm/dsh-agent-team@0.2.0
54
56
  ```
55
57
 
58
+ 这里刻意写明确切版本:pnpm 会跳过发布不满 24 小时的版本,不带版本号的 `@latest` 在发布当天会装到上一版。
59
+
56
60
  ### 2. 启动 Web UI
57
61
 
58
62
  ```sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wowyuarm/dsh-agent-team",
3
- "version": "0.1.15",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "description": "A persistent agent team for long-running collaboration in DeepSeek Harness",
6
6
  "repository": {
@@ -27,9 +27,10 @@
27
27
  "check:core-skills": "node scripts/check-core-skills.mjs",
28
28
  "check:boundaries": "node scripts/check-package-boundaries.mjs",
29
29
  "check:versions": "node scripts/check-version-consistency.mjs",
30
+ "check:facades": "node scripts/sync-paths.mjs --check",
30
31
  "check:artifact": "node scripts/check-artifact.mjs .",
31
32
  "check:public-baseline": "node scripts/check-public-baseline.mjs .",
32
- "test": "npm run generate:typert && npm run check:docs && npm run check:core-skills && npm run check:boundaries && npm run check:versions && vitest run",
33
+ "test": "npm run generate:typert && npm run check:facades && npm run check:docs && npm run check:core-skills && npm run check:boundaries && npm run check:versions && vitest run",
33
34
  "test:browser": "npm run build && node scripts/run-browser-test.mjs",
34
35
  "preview": "node scripts/check-preview-credentials.mjs && npm run build && node scripts/run-preview.mjs",
35
36
  "preview:ui": "npm run build && node scripts/run-ui-preview.mjs",
@@ -60,7 +61,7 @@
60
61
  }
61
62
  },
62
63
  "dependencies": {
63
- "@wowyuarm/dsh-context-continuity": "^0.1.4",
64
+ "@wowyuarm/dsh-context-continuity": "^0.1.6",
64
65
  "yaml": "^2.9.1",
65
66
  "zod": "^4.4.3"
66
67
  },
@@ -145,50 +146,51 @@
145
146
  },
146
147
  "peerDependencies": {
147
148
  "@deepseek-ai/cordis": "^4.0.1",
148
- "@deepseek-ai/dsh-agent": ">=0.1.7-rc.1 <0.1.8",
149
- "@deepseek-ai/dsh-agent-default-model": ">=0.1.7-rc.1 <0.1.8",
150
- "@deepseek-ai/dsh-agent-preset": ">=0.1.7-rc.1 <0.1.8",
151
- "@deepseek-ai/dsh-agent-preset-registry": ">=0.1.7-rc.1 <0.1.8",
152
- "@deepseek-ai/dsh-agent-tool-presentation": ">=0.1.7-rc.1 <0.1.8",
153
- "@deepseek-ai/dsh-api-remotes": ">=0.1.7-rc.1 <0.1.8",
154
- "@deepseek-ai/dsh-api-session-controller": ">=0.1.7-rc.1 <0.1.8",
155
- "@deepseek-ai/dsh-api-workspace-controller": ">=0.1.7-rc.1 <0.1.8",
156
- "@deepseek-ai/dsh-brand": ">=0.1.7-rc.1 <0.1.8",
157
- "@deepseek-ai/dsh-client-locale": ">=0.1.7-rc.1 <0.1.8",
158
- "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.7-rc.1 <0.1.8",
159
- "@deepseek-ai/dsh-client-ui-input-trigger": ">=0.1.7-rc.1 <0.1.8",
160
- "@deepseek-ai/dsh-client-ui-layout": ">=0.1.7-rc.1 <0.1.8",
161
- "@deepseek-ai/dsh-client-ui-primitives": ">=0.1.7-rc.1 <0.1.8",
162
- "@deepseek-ai/dsh-client-ui-renderer": ">=0.1.7-rc.1 <0.1.8",
163
- "@deepseek-ai/dsh-client-ui-settings-general": ">=0.1.7-rc.1 <0.1.8",
164
- "@deepseek-ai/dsh-client-ui-sidebar": ">=0.1.7-rc.1 <0.1.8",
165
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.7-rc.1 <0.1.8",
166
- "@deepseek-ai/dsh-client-ui-workspace": ">=0.1.7-rc.1 <0.1.8",
167
- "@deepseek-ai/dsh-command-compact": ">=0.1.7-rc.1 <0.1.8",
168
- "@deepseek-ai/dsh-compaction": ">=0.1.7-rc.1 <0.1.8",
169
- "@deepseek-ai/dsh-home-paths": ">=0.1.7-rc.1 <0.1.8",
170
- "@deepseek-ai/dsh-invariants": ">=0.1.7-rc.1 <0.1.8",
171
- "@deepseek-ai/dsh-llm": ">=0.1.7-rc.1 <0.1.8",
172
- "@deepseek-ai/dsh-sandbox-policy": ">=0.1.7-rc.1 <0.1.8",
173
- "@deepseek-ai/dsh-scope": ">=0.1.7-rc.1 <0.1.8",
174
- "@deepseek-ai/dsh-session": ">=0.1.7-rc.1 <0.1.8",
175
- "@deepseek-ai/dsh-session-format-catalog": ">=0.1.7-rc.1 <0.1.8",
176
- "@deepseek-ai/dsh-session-persistence": ">=0.1.7-rc.1 <0.1.8",
177
- "@deepseek-ai/dsh-skill": ">=0.1.7-rc.1 <0.1.8",
178
- "@deepseek-ai/dsh-skill-filesystem": ">=0.1.7-rc.1 <0.1.8",
179
- "@deepseek-ai/dsh-storage-domain": ">=0.1.7-rc.1 <0.1.8",
180
- "@deepseek-ai/dsh-token-meter": ">=0.1.7-rc.1 <0.1.8",
181
- "@deepseek-ai/dsh-tool-bash": ">=0.1.7-rc.1 <0.1.8",
182
- "@deepseek-ai/dsh-tool-fs": ">=0.1.7-rc.1 <0.1.8",
183
- "@deepseek-ai/dsh-tool-fs-search": ">=0.1.7-rc.1 <0.1.8",
184
- "@deepseek-ai/dsh-tool-jobs": ">=0.1.7-rc.1 <0.1.8",
185
- "@deepseek-ai/dsh-tool-pwsh": ">=0.1.7-rc.1 <0.1.8",
186
- "@deepseek-ai/dsh-tool-skill": ">=0.1.7-rc.1 <0.1.8",
187
- "@deepseek-ai/dsh-tool-todo": ">=0.1.7-rc.1 <0.1.8",
188
- "@deepseek-ai/dsh-tool-web": ">=0.1.7-rc.1 <0.1.8",
189
- "@deepseek-ai/dsh-tools": ">=0.1.7-rc.1 <0.1.8",
190
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.7-rc.1 <0.1.8",
191
- "@deepseek-ai/dsh-workspace": ">=0.1.7-rc.1 <0.1.8",
149
+ "@deepseek-ai/dsh-agent": ">=0.2.0-rc.1 <0.2.1",
150
+ "@deepseek-ai/dsh-agent-default-model": ">=0.2.0-rc.1 <0.2.1",
151
+ "@deepseek-ai/dsh-agent-preset": ">=0.2.0-rc.1 <0.2.1",
152
+ "@deepseek-ai/dsh-agent-preset-registry": ">=0.2.0-rc.1 <0.2.1",
153
+ "@deepseek-ai/dsh-agent-tool-presentation": ">=0.2.0-rc.1 <0.2.1",
154
+ "@deepseek-ai/dsh-app-boot": ">=0.2.0-rc.1 <0.2.1",
155
+ "@deepseek-ai/dsh-api-remotes": ">=0.2.0-rc.1 <0.2.1",
156
+ "@deepseek-ai/dsh-api-session-controller": ">=0.2.0-rc.1 <0.2.1",
157
+ "@deepseek-ai/dsh-api-workspace-controller": ">=0.2.0-rc.1 <0.2.1",
158
+ "@deepseek-ai/dsh-brand": ">=0.2.0-rc.1 <0.2.1",
159
+ "@deepseek-ai/dsh-client-locale": ">=0.2.0-rc.1 <0.2.1",
160
+ "@deepseek-ai/dsh-client-ui-conversation": ">=0.2.0-rc.1 <0.2.1",
161
+ "@deepseek-ai/dsh-client-ui-input-trigger": ">=0.2.0-rc.1 <0.2.1",
162
+ "@deepseek-ai/dsh-client-ui-layout": ">=0.2.0-rc.1 <0.2.1",
163
+ "@deepseek-ai/dsh-client-ui-primitives": ">=0.2.0-rc.1 <0.2.1",
164
+ "@deepseek-ai/dsh-client-ui-renderer": ">=0.2.0-rc.1 <0.2.1",
165
+ "@deepseek-ai/dsh-client-ui-settings-general": ">=0.2.0-rc.1 <0.2.1",
166
+ "@deepseek-ai/dsh-client-ui-sidebar": ">=0.2.0-rc.1 <0.2.1",
167
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.2.0-rc.1 <0.2.1",
168
+ "@deepseek-ai/dsh-client-ui-workspace": ">=0.2.0-rc.1 <0.2.1",
169
+ "@deepseek-ai/dsh-command-compact": ">=0.2.0-rc.1 <0.2.1",
170
+ "@deepseek-ai/dsh-compaction": ">=0.2.0-rc.1 <0.2.1",
171
+ "@deepseek-ai/dsh-home-paths": ">=0.2.0-rc.1 <0.2.1",
172
+ "@deepseek-ai/dsh-invariants": ">=0.2.0-rc.1 <0.2.1",
173
+ "@deepseek-ai/dsh-llm": ">=0.2.0-rc.1 <0.2.1",
174
+ "@deepseek-ai/dsh-sandbox-policy": ">=0.2.0-rc.1 <0.2.1",
175
+ "@deepseek-ai/dsh-scope": ">=0.2.0-rc.1 <0.2.1",
176
+ "@deepseek-ai/dsh-session": ">=0.2.0-rc.1 <0.2.1",
177
+ "@deepseek-ai/dsh-session-format-catalog": ">=0.2.0-rc.1 <0.2.1",
178
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.0-rc.1 <0.2.1",
179
+ "@deepseek-ai/dsh-skill": ">=0.2.0-rc.1 <0.2.1",
180
+ "@deepseek-ai/dsh-skill-filesystem": ">=0.2.0-rc.1 <0.2.1",
181
+ "@deepseek-ai/dsh-storage-domain": ">=0.2.0-rc.1 <0.2.1",
182
+ "@deepseek-ai/dsh-token-meter": ">=0.2.0-rc.1 <0.2.1",
183
+ "@deepseek-ai/dsh-tool-bash": ">=0.2.0-rc.1 <0.2.1",
184
+ "@deepseek-ai/dsh-tool-fs": ">=0.2.0-rc.1 <0.2.1",
185
+ "@deepseek-ai/dsh-tool-fs-search": ">=0.2.0-rc.1 <0.2.1",
186
+ "@deepseek-ai/dsh-tool-jobs": ">=0.2.0-rc.1 <0.2.1",
187
+ "@deepseek-ai/dsh-tool-pwsh": ">=0.2.0-rc.1 <0.2.1",
188
+ "@deepseek-ai/dsh-tool-skill": ">=0.2.0-rc.1 <0.2.1",
189
+ "@deepseek-ai/dsh-tool-todo": ">=0.2.0-rc.1 <0.2.1",
190
+ "@deepseek-ai/dsh-tool-web": ">=0.2.0-rc.1 <0.2.1",
191
+ "@deepseek-ai/dsh-tools": ">=0.2.0-rc.1 <0.2.1",
192
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.0-rc.1 <0.2.1",
193
+ "@deepseek-ai/dsh-workspace": ">=0.2.0-rc.1 <0.2.1",
192
194
  "react": "^18.2.0"
193
195
  },
194
196
  "keywords": [
@@ -34,19 +34,24 @@ export const AGENT_TEAM_V3_RENAMED_KIND = `plugin:${AGENT_TEAM_PLUGIN_ID}`;
34
34
  /** The kind the read-time conversion renames one producer's released V3 sources into. */
35
35
  export const v3RenamedSourceKind = (producer) => `plugin:${producer}`;
36
36
  /**
37
- * Whether one message source carries this plugin's own attribution. Both
38
- * identities are matched by exact kind equality — never by a `plugin:`
39
- * prefix test, which would claim third-party producers' rows as Team facts.
37
+ * Whether one source kind is a producer's own: the kind it writes now and the
38
+ * read-time conversion of its released V3 history. Both identities are matched
39
+ * by exact kind equality — never by a `plugin:` prefix test, which would claim
40
+ * third-party producers' rows as that producer's facts.
40
41
  */
42
+ export function matchesProducerKind(kind, producer) {
43
+ return kind === producer || kind === v3RenamedSourceKind(producer);
44
+ }
45
+ /** Whether one message source carries this plugin's own attribution. */
41
46
  export function isAgentTeamSource(source) {
42
- return source.kind === AGENT_TEAM_PLUGIN_ID || source.kind === AGENT_TEAM_V3_RENAMED_KIND;
47
+ return matchesProducerKind(source.kind, AGENT_TEAM_PLUGIN_ID);
43
48
  }
44
49
  /**
45
50
  * Whether one source kind carries this plugin's attribution, for call sites
46
- * whose sources arrive untyped. Exact identities only, never a prefix test.
51
+ * whose sources arrive untyped.
47
52
  */
48
53
  export function isAgentTeamSourceKind(kind) {
49
- return kind === AGENT_TEAM_PLUGIN_ID || kind === AGENT_TEAM_V3_RENAMED_KIND;
54
+ return matchesProducerKind(kind, AGENT_TEAM_PLUGIN_ID);
50
55
  }
51
56
  /** Handoff snapshot section name carrying the model-authored prose. */
52
57
  export const HANDOFF_SECTION_NAME = 'HANDOFF';
@@ -0,0 +1,102 @@
1
+ /** The one local environment check: how the running DSH compares to the line this bundle declares. */
2
+ import { evaluatePluginCompatibility, getDshRuntimeVersion } from '@deepseek-ai/dsh-app-boot';
3
+ import { bundleVersionOf, readInstalledManifest } from "./installed-manifest.js";
4
+ /** Prefix of every peer that sits on the DSH version line; the bare scope is not one. */
5
+ const DSH_PEER_PREFIX = '@deepseek-ai/dsh-';
6
+ /** The `>=lower <upper>` shape every DSH peer carries; a comparator series, not a single version. */
7
+ const SUPPORT_RANGE_SHAPE = /^>=(\S+)\s+<(\S+)$/u;
8
+ /**
9
+ * Read the support line from the installed manifest's DSH peers, or nothing.
10
+ *
11
+ * Every `@deepseek-ai/dsh-*` peer must carry exactly the same range, and it must
12
+ * be a `>=<lower> <upper>` series: the version line is the manifest's own claim
13
+ * and a partially drifted manifest cannot state one. `scripts/check-version-consistency.mjs`
14
+ * holds the same shape to the certified baseline, so a run of this bundle
15
+ * states the same line its README does. Returns `undefined` when the peers
16
+ * disagree or the shape is unreadable.
17
+ */
18
+ export function supportRangeOf(manifest) {
19
+ const fields = manifest;
20
+ if (typeof fields.peerDependencies !== 'object' || fields.peerDependencies === null)
21
+ return undefined;
22
+ const ranges = new Set();
23
+ for (const [name, range] of Object.entries(fields.peerDependencies)) {
24
+ if (!name.startsWith(DSH_PEER_PREFIX))
25
+ continue;
26
+ if (typeof range !== 'string')
27
+ return undefined;
28
+ ranges.add(range);
29
+ }
30
+ const distinct = [...ranges];
31
+ if (distinct.length !== 1)
32
+ return undefined;
33
+ const shape = SUPPORT_RANGE_SHAPE.exec(distinct[0]);
34
+ if (shape === null)
35
+ return undefined;
36
+ return Object.freeze({ lower: shape[1], upper: shape[2] });
37
+ }
38
+ /**
39
+ * Judge one already-resolved triple, so every branch is testable without an
40
+ * environment. The verdict comes from the Harness's own
41
+ * `evaluatePluginCompatibility` — the plugin manager decides installability with
42
+ * that same function, and prerelease ordering is exactly what a hand-written
43
+ * comparison gets wrong.
44
+ *
45
+ * @param manifest - the installed bundle's parsed manifest, the source of the declared support line.
46
+ * @param runtimeVersion - the running DSH version, or `'unknown'` when it could not be read.
47
+ * @param bundleVersion - the installed bundle version, or `'unknown'`.
48
+ * @param unavailable - why the running version is not known, recorded when it is `'unknown'`.
49
+ * @returns the report the settings page renders.
50
+ */
51
+ export function evaluateEnvironment(manifest, runtimeVersion, bundleVersion, unavailable) {
52
+ const range = supportRangeOf(manifest);
53
+ const base = {
54
+ bundleVersion,
55
+ ...(range === undefined ? {} : { certifiedDshVersion: range.lower, supportRange: range }),
56
+ };
57
+ if (runtimeVersion === 'unknown') {
58
+ return Object.freeze({ ...base, verdict: 'undetermined', reason: unavailable ?? 'the running dsh version is unavailable' });
59
+ }
60
+ const running = { ...base, dshVersion: runtimeVersion };
61
+ // A real violation is answered first, and it is never downgraded to
62
+ // `undetermined`: a peer that drifted to a range of its own is a genuine
63
+ // "not satisfied" even though no single range can then be stated for the
64
+ // page. Only when nothing is violated does the question become whether the
65
+ // declared line can be stated at all.
66
+ // One evaluation covers every dsh peer in the manifest, and `range` above is
67
+ // derived from exactly those peers: re-judging the range it prints could not
68
+ // change this answer.
69
+ const satisfied = evaluatePluginCompatibility(manifest, {}, runtimeVersion) === undefined;
70
+ if (!satisfied)
71
+ return Object.freeze({ ...running, verdict: 'out-of-range' });
72
+ // Nothing is violated but there is no line to state: the page withholds the
73
+ // range rather than inventing one.
74
+ if (range === undefined) {
75
+ return Object.freeze({ ...base, verdict: 'undetermined', reason: 'the declared dsh support range is unreadable or drifted' });
76
+ }
77
+ return Object.freeze({ ...running, verdict: 'ok' });
78
+ }
79
+ /**
80
+ * Read this Host's own environment: the installed bundle version, the declared
81
+ * support line, and how the running DSH compares to it.
82
+ *
83
+ * The certified DSH version is the range's lower bound, never the newest cut
84
+ * tested against it: `docs/dsh-release-compatibility.md` §4 records a baseline
85
+ * inside the declared range without moving the peers, so every version spot
86
+ * keeps naming the bound. Reading it from anywhere else would reintroduce the
87
+ * hand-maintained constant this check exists to remove.
88
+ *
89
+ * @returns the report, with `verdict: 'undetermined'` for any fact not established.
90
+ */
91
+ export function reportEnvironment() {
92
+ const manifest = readInstalledManifest() ?? {};
93
+ const bundleVersion = bundleVersionOf(manifest);
94
+ let runtime;
95
+ try {
96
+ runtime = getDshRuntimeVersion();
97
+ }
98
+ catch (error) {
99
+ return evaluateEnvironment(manifest, 'unknown', bundleVersion, error instanceof Error ? error.message : String(error));
100
+ }
101
+ return evaluateEnvironment(manifest, runtime, bundleVersion);
102
+ }
@@ -1,8 +1,6 @@
1
- import { readFileSync } from 'node:fs';
2
- import { dirname, resolve } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
1
  import z from '@deepseek-ai/schemastery';
5
2
  import { parse } from 'yaml';
3
+ import { bundleVersionOf, readInstalledManifest } from "./installed-manifest.js";
6
4
  /**
7
5
  * Human identity profile: the one configurable display name plus the avatar
8
6
  * reference. `member:human` stays the durable identity everywhere; only the
@@ -17,6 +15,12 @@ import { parse } from 'yaml';
17
15
  * - avatar bytes live under a persistent directory below; the profile holds
18
16
  * only the reference, never a data URL. The composer attachment cache is
19
17
  * TTL-bound and must not hold avatar bytes.
18
+ *
19
+ * The legacy-section adoption below is transitional: it exists for installs
20
+ * upgraded from a pre-rc.1 profile document, and retires — with
21
+ * `LEGACY_HUMAN_PROFILE_SECTION`, `parseLegacyHumanProfile`, and
22
+ * `planLegacyAdoption` — once such installs are no longer supported. Nothing
23
+ * else in the Host reads that document.
20
24
  */
21
25
  /**
22
26
  * Settings namespace of the Human profile: the Team Host row's id in
@@ -33,36 +37,18 @@ export const HUMAN_PROFILE_DEFAULT_NAME = 'human';
33
37
  export const HUMAN_PROFILE_REPO_URL = 'https://github.com/wowyuarm/dsh-agent-team';
34
38
  /**
35
39
  * Bundle version shown in the settings footnote, and the current side of the
36
- * update check: the version of the package THIS Host runs from — read from the
37
- * installed manifest, so a `link:` checkout under the development profile and a
38
- * registry tarball under stable each state their own truth. It is not a
39
- * hand-maintained string: the 0.1.14 bundle shipped with `0.1.13` written in
40
- * it, which made the footnote name the previous release and the update check
41
- * offer the release the user already had.
40
+ * update check: the version of the package THIS Host runs from — the installed
41
+ * manifest's, read once at load through `./installed-manifest.ts`, so a `link:`
42
+ * checkout under the development profile and a registry tarball under stable
43
+ * each state their own truth. It is not a hand-maintained string: the 0.1.14
44
+ * bundle shipped with `0.1.13` written in it, which made the footnote name the
45
+ * previous release and the update check offer the release the user already had.
42
46
  *
43
- * Resolved once at load, three levels above this module — `packages/agent-team/{src,lib}`
44
- * sits that deep in both layouts, the same relative positioning
45
- * `member-runtime.ts` uses to find `core-skills`. An unreadable or malformed
46
- * manifest degrades to `'unknown'`: the Remote's `version: string` contract
47
- * holds and the update comparison simply compares nothing. The footnote is
48
- * informational only and never gates behavior.
47
+ * An unreadable or malformed manifest degrades to `'unknown'`: the Remote's
48
+ * `version: string` contract holds and the update comparison simply compares
49
+ * nothing. The footnote is informational only and never gates behavior.
49
50
  */
50
- export const HUMAN_PROFILE_VERSION = readInstalledBundleVersion();
51
- /** Version of the manifest this package installed from, or `'unknown'`. */
52
- function readInstalledBundleVersion() {
53
- try {
54
- const manifestPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../../package.json');
55
- const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
56
- if (typeof manifest.version === 'string' && manifest.version !== '')
57
- return manifest.version;
58
- }
59
- catch {
60
- // Reading our own manifest must never fail the Host boot: a host that
61
- // cannot find its own package.json is a broken install, and the footnote
62
- // reporting 'unknown' says so more honestly than a stale number.
63
- }
64
- return 'unknown';
65
- }
51
+ export const HUMAN_PROFILE_VERSION = bundleVersionOf(readInstalledManifest());
66
52
  /**
67
53
  * Schemastery schema of the Human profile: the Team Host row's Config. Both
68
54
  * fields are volatile, which is what lets an edit reach the running Host
@@ -139,12 +125,12 @@ export function parseLegacyHumanProfile(yamlText) {
139
125
  * Decide what adoption may write: nothing unless the stored profile is still
140
126
  * the pristine default, so a value the Human re-entered after the upgrade
141
127
  * always wins, and never the legacy default name itself. The current profile
142
- * carries explicit `undefined` on `avatarRef` — the Host getter spreads a
143
- * settings read — and so is not `HumanProfile` under
144
- * exactOptionalPropertyTypes; taking that shape directly keeps the Host call
145
- * cast-free. The returned fields are exactly the ops the profile page would
146
- * have written; byte existence for `avatarRef` is the caller's I/O and must
147
- * already hold.
128
+ * carries explicit `undefined` on `avatarRef` — the Host getter spreads the
129
+ * live Config read — and that shape does not satisfy the optional-property
130
+ * form under exactOptionalPropertyTypes; taking it directly keeps the Host
131
+ * call cast-free. The returned fields are exactly the ops the profile page
132
+ * would have written; byte existence for `avatarRef` is the caller's I/O and
133
+ * must already hold.
148
134
  */
149
135
  export function planLegacyAdoption(current, legacy) {
150
136
  if (current.name !== HUMAN_PROFILE_DEFAULT_NAME || current.avatarRef !== undefined)
@@ -52,6 +52,7 @@ import { Session, SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session';
52
52
  import { setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy';
53
53
  import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
54
54
  import { ATTACHMENT_MAX_BYTES, attachmentPayloadPath, attachmentsRoot, copyPathAttachment, newAttachmentId, readAttachment, sanitizeMediaType, sweepAttachmentCache, validatePathAttachment, writeAttachment } from "./attachments.js";
55
+ import { reportEnvironment } from "./environment-check.js";
55
56
  import { HUMAN_PROFILE_DEFAULT_NAME, HUMAN_PROFILE_REPO_URL, HUMAN_PROFILE_SETTINGS_NAMESPACE, HUMAN_PROFILE_SETTINGS_SCHEMA, HUMAN_PROFILE_VERSION, assertValidHumanName, normalizeHumanName, parseLegacyHumanProfile, planLegacyAdoption } from "./human-profile.js";
56
57
  import { humanAvatarsRoot, readHumanAvatar, removeHumanAvatar, writeHumanAvatar } from "./human-avatar.js";
57
58
  import { createHumanUpdateChecker } from "./human-update-check.js";
@@ -91,7 +92,8 @@ class PresetCompositionError extends Error {
91
92
  const MAX_CHECKPOINT_NAME_CHARS = 120;
92
93
  /** Default and maximum number of timeline items one query returns. */
93
94
  const DEFAULT_TIMELINE_LIMIT = 12;
94
- const MAX_TIMELINE_LIMIT = 24;
95
+ /** The widest timeline window there is; the ref gate reads at this width so no printed ref is unreachable. */
96
+ export const MAX_TIMELINE_LIMIT = 24;
95
97
  /**
96
98
  * Archived generations the timeline and seed resolution walk: the timeline
97
99
  * reads this many ancestors behind the current generation, and the seed guard
@@ -195,6 +197,7 @@ let AgentTeam = (() => {
195
197
  let _sendMessage_decorators;
196
198
  let _putAttachment_decorators;
197
199
  let _getAttachment_decorators;
200
+ let _environmentForClient_decorators;
198
201
  let _humanProfileForClient_decorators;
199
202
  let _putHumanAvatar_decorators;
200
203
  let _getHumanAvatar_decorators;
@@ -231,6 +234,7 @@ let AgentTeam = (() => {
231
234
  _sendMessage_decorators = [Remote('sendMessage')];
232
235
  _putAttachment_decorators = [Remote('putAttachment')];
233
236
  _getAttachment_decorators = [Remote('getAttachment')];
237
+ _environmentForClient_decorators = [Remote('environment')];
234
238
  _humanProfileForClient_decorators = [Remote('humanProfile')];
235
239
  _putHumanAvatar_decorators = [Remote('putHumanAvatar')];
236
240
  _getHumanAvatar_decorators = [Remote('getHumanAvatar')];
@@ -264,6 +268,7 @@ let AgentTeam = (() => {
264
268
  __esDecorate(this, null, _sendMessage_decorators, { kind: "method", name: "sendMessage", static: false, private: false, access: { has: obj => "sendMessage" in obj, get: obj => obj.sendMessage }, metadata: _metadata }, null, _instanceExtraInitializers);
265
269
  __esDecorate(this, null, _putAttachment_decorators, { kind: "method", name: "putAttachment", static: false, private: false, access: { has: obj => "putAttachment" in obj, get: obj => obj.putAttachment }, metadata: _metadata }, null, _instanceExtraInitializers);
266
270
  __esDecorate(this, null, _getAttachment_decorators, { kind: "method", name: "getAttachment", static: false, private: false, access: { has: obj => "getAttachment" in obj, get: obj => obj.getAttachment }, metadata: _metadata }, null, _instanceExtraInitializers);
271
+ __esDecorate(this, null, _environmentForClient_decorators, { kind: "method", name: "environmentForClient", static: false, private: false, access: { has: obj => "environmentForClient" in obj, get: obj => obj.environmentForClient }, metadata: _metadata }, null, _instanceExtraInitializers);
267
272
  __esDecorate(this, null, _humanProfileForClient_decorators, { kind: "method", name: "humanProfileForClient", static: false, private: false, access: { has: obj => "humanProfileForClient" in obj, get: obj => obj.humanProfileForClient }, metadata: _metadata }, null, _instanceExtraInitializers);
268
273
  __esDecorate(this, null, _putHumanAvatar_decorators, { kind: "method", name: "putHumanAvatar", static: false, private: false, access: { has: obj => "putHumanAvatar" in obj, get: obj => obj.putHumanAvatar }, metadata: _metadata }, null, _instanceExtraInitializers);
269
274
  __esDecorate(this, null, _getHumanAvatar_decorators, { kind: "method", name: "getHumanAvatar", static: false, private: false, access: { has: obj => "getHumanAvatar" in obj, get: obj => obj.getHumanAvatar }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -445,7 +450,8 @@ let AgentTeam = (() => {
445
450
  * The two judgements the Config schema cannot make about a Human name: the
446
451
  * same non-empty floor as Member handles, plus global uniqueness against live
447
452
  * Members. `setHumanProfile` runs it before the write, so a colliding rename
448
- * rejects instead of persisting.
453
+ * rejects instead of persisting. It reads the one field it judges, so the
454
+ * caller passes the name alone rather than a whole profile shape.
449
455
  */
450
456
  validateHumanProfile(value) {
451
457
  const name = assertValidHumanName(value.name);
@@ -1316,6 +1322,26 @@ let AgentTeam = (() => {
1316
1322
  * `updateAvailable` is false until a background refresh actually observes a
1317
1323
  * newer published release.
1318
1324
  */
1325
+ /**
1326
+ * Local environment check for the settings page: which DSH line this Host
1327
+ * runs against, and whether that line is inside the range this bundle
1328
+ * declares. Human-scoped and read-only — the Client renders the verdict and
1329
+ * states the range in words, and nothing here is written back.
1330
+ *
1331
+ * Synchronous like `humanProfile`: both read the installed manifest once and
1332
+ * settle immediately, and the verdict comes from the Harness's own
1333
+ * compatibility evaluator rather than a second version comparison written
1334
+ * here.
1335
+ *
1336
+ * The wire type and `EnvironmentReport` keep separate top-level field lists on
1337
+ * purpose: the contract states every fact as optional because a Transport may
1338
+ * withhold any of them, while the report is the stricter thing the checker
1339
+ * guarantees. This assignment is where the compiler holds the two together,
1340
+ * and a new required contract field fails it here.
1341
+ */
1342
+ environmentForClient(_request) {
1343
+ return Object.freeze(reportEnvironment());
1344
+ }
1319
1345
  humanProfileForClient(_request) {
1320
1346
  const profile = this.humanProfile();
1321
1347
  const update = this.humanUpdateCheck.snapshot();
@@ -0,0 +1,32 @@
1
+ /** The installed bundle's own manifest, read once for both facts taken from it. */
2
+ import { readFileSync } from 'node:fs';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ /**
6
+ * Parsed manifest of the installed bundle, or `undefined` when it cannot be
7
+ * read.
8
+ *
9
+ * Three levels up: `packages/agent-team/{src,lib}` sits that deep in both a
10
+ * `link:` checkout and a registry tarball, so a development install and a
11
+ * published one each state their own truth. An unreadable or malformed manifest
12
+ * is a broken install, not a failure of whatever asked: every caller degrades to
13
+ * `'unknown'` or withholds the line it would have stated, so reading this can
14
+ * never fail the Host boot.
15
+ */
16
+ export function readInstalledManifest() {
17
+ try {
18
+ const manifestPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../../package.json');
19
+ const parsed = JSON.parse(readFileSync(manifestPath, 'utf8'));
20
+ return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
21
+ ? parsed
22
+ : undefined;
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ /** Version of the manifest this package installed from, or `'unknown'`. */
29
+ export function bundleVersionOf(manifest) {
30
+ const version = manifest?.version;
31
+ return typeof version === 'string' && version !== '' ? version : 'unknown';
32
+ }
@@ -1586,6 +1586,7 @@ export class AgentTeamLedger {
1586
1586
  })),
1587
1587
  taskNumbers: Object.freeze(visibleTasks.map(task => Object.freeze({ taskRef: task.taskRef, taskNumber: taskNumbers.get(task.taskRef) ?? 0 }))),
1588
1588
  items: Object.freeze(items),
1589
+ activeTaskThreads: this.activeTaskThreads(visibleTasks, taskNumbers),
1589
1590
  claims: this.claimsForVisibleTasks(visibleTasks),
1590
1591
  activities: Object.freeze(selected.filter((fact) => fact.kind === 'activity').map(fact => fact.activity)),
1591
1592
  cursor: nextCursor,
@@ -3172,6 +3173,40 @@ export class AgentTeamLedger {
3172
3173
  const refs = new Set(tasks.map(task => task.taskRef));
3173
3174
  return Object.freeze([...this.state.claims.values()].filter(claim => refs.has(claim.taskRef)));
3174
3175
  }
3176
+ /**
3177
+ * The team_view「活跃 task thread」radar for a Workspace: every visible Task
3178
+ * still in flight — in_progress or in_review, the two states a live Claim
3179
+ * keeps a Task in — newest activity first. Unlike `recentInboxItems`, this is
3180
+ * not a participation slice: admission is the Task being active in a Channel
3181
+ * the reader can see, whether or not the reader wrote in it, and a Thread
3182
+ * still holding the reader's unread is not excluded. Rows carry the same
3183
+ * `liveClaimOwners`口径 the Inbox and Channel feed already use, so 「谁在这个
3184
+ * Task 上」 never gains a second definition. Archived Channels are already
3185
+ * absent from `visibleTasks`, so their Tasks never reach here.
3186
+ */
3187
+ activeTaskThreads(visibleTasks, taskNumbers) {
3188
+ const rows = [];
3189
+ for (const task of visibleTasks) {
3190
+ if (task.status !== 'in_progress' && task.status !== 'in_review')
3191
+ continue;
3192
+ const thread = this.state.threads.get(task.threadRef);
3193
+ if (thread === undefined)
3194
+ continue;
3195
+ const newest = (this.state.factsByThread.get(thread.threadRef) ?? []).at(-1);
3196
+ if (newest === undefined)
3197
+ continue;
3198
+ const taskNumber = taskNumbers.get(task.taskRef);
3199
+ rows.push({
3200
+ row: Object.freeze({ taskRef: task.taskRef, threadRef: thread.threadRef, channelRef: task.channelRef,
3201
+ ...(taskNumber === undefined ? {} : { taskNumber }), status: task.status,
3202
+ subject: boundedInboxPreview(this.threadAnchor(thread.threadRef).body),
3203
+ members: this.liveClaimOwners(task), lastActivityAt: newest.occurredAt }),
3204
+ sequence: newest.sequence,
3205
+ });
3206
+ }
3207
+ rows.sort((left, right) => right.sequence - left.sequence || left.row.threadRef.localeCompare(right.row.threadRef));
3208
+ return Object.freeze(rows.map(entry => entry.row));
3209
+ }
3175
3210
  tasksForClaims(claims, projected) {
3176
3211
  return Object.freeze([...new Set(claims.map(claim => claim.taskRef))].map(taskRef => {
3177
3212
  const task = this.state.tasks.get(taskRef);
@@ -2,7 +2,7 @@ import { readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { createUserMessage } from '@deepseek-ai/dsh-llm';
4
4
  import { SessionLogOffset } from '@deepseek-ai/dsh-session';
5
- import { v3RenamedSourceKind } from "./context-source.js";
5
+ import { matchesProducerKind } from "./context-source.js";
6
6
  import { memberMemoryDirectoryPath } from "./member-runtime.js";
7
7
  export const name = 'wowyuarm-agent-team-member-context';
8
8
  const MAX_MEMORY_BYTES = 16 * 1024;
@@ -43,9 +43,7 @@ export function apply(ctx) {
43
43
  // number domains explicit (the seq = log.length contiguity contract).
44
44
  const event = agent.session.snapshotEvents(SessionLogOffset(sequence), SessionLogOffset(sequence + 1))[0];
45
45
  return event?.type === 'user/message'
46
- // Both identities are this producer's own: the kind written now and
47
- // the read-time conversion's rename of this producer's V3 history.
48
- && (event.data.source.kind === name || event.data.source.kind === v3RenamedSourceKind(name))
46
+ && matchesProducerKind(event.data.source.kind, name)
49
47
  && event.data.content[0]?.type === 'text'
50
48
  ? [event.data.content[0].text]
51
49
  : [];
@@ -27,7 +27,7 @@
27
27
  * @module @wowyuarm/dsh-agent-team/member-time-context
28
28
  */
29
29
  import { createUserMessage } from '@deepseek-ai/dsh-llm';
30
- import { v3RenamedSourceKind } from "./context-source.js";
30
+ import { matchesProducerKind } from "./context-source.js";
31
31
  import { formatTeamDuration, formatTeamTimestamp } from "./time-format.js";
32
32
  import { advanceOwnedSessionEventCursor } from "./session-event-cursor.js";
33
33
  export const name = 'wowyuarm-agent-team-member-time-context';
@@ -50,9 +50,7 @@ export function applyClockEvent(state, event) {
50
50
  return state.openTurn === -1 ? state : { ...state, lastTurnInjectionTime: null, openTurn: -1 };
51
51
  case 'user/message': {
52
52
  const source = event.data.source;
53
- // Both identities are this producer's own: the kind written now and the
54
- // read-time conversion's rename of this producer's V3 history.
55
- const injected = source.kind === name || source.kind === v3RenamedSourceKind(name);
53
+ const injected = matchesProducerKind(source.kind, name);
56
54
  const withMessage = state.lastMessageTime === event.time ? state : { ...state, lastMessageTime: event.time };
57
55
  if (!injected)
58
56
  return withMessage;