feat(skill): extract the workspace-dependencies tool into a package

Move `load_workspace_dependencies` and the primary-runtime payload logic out
of `apps/desktop-host` into `@deepseek-ai/dsh-tool-workspace-dependencies`
so profiles other than Desktop can mount the same tool. Desktop keeps its
behavior through the package.

Two additions for carriers that are not Electron applications:

- `root` is optional: without it the payload is validated and used in place
  (no copy under the Harness home), which suits read-only container layers.
- `runtime.json` accepts `linux`, and the `node` / `pnpm` components are
  optional; the returned paths and the tool output omit what the payload
  does not ship.

The `sdk` profile mounts the tool and `@deepseek-ai/dsh-skill-office` when
`DSH_PRIMARY_RUNTIME` names a payload directory (its sibling `office-skills/`
carries the skill resources); both rows stay disabled otherwise. Both
packages join the Python runtime closure.
This commit is contained in:
fz committed 2026-09-17 18:16:47 +08:00
1 parent 5b1a2d8613
commit 6e49ccad18
29 files changed
+642 -96

No files matched your search

+2
View File
@@ -70,6 +70,8 @@
"@deepseek-ai/dsh-session-reference": "workspace:^",
"@deepseek-ai/dsh-skill": "workspace:^",
"@deepseek-ai/dsh-skill-filesystem": "workspace:^",
"@deepseek-ai/dsh-skill-office": "workspace:^",
"@deepseek-ai/dsh-tool-workspace-dependencies": "workspace:^",
"@deepseek-ai/dsh-terminal": "workspace:^",
"@deepseek-ai/dsh-terminal-bash": "workspace:^",
"@deepseek-ai/dsh-time-context": "workspace:^",
+1 -1
View File
@@ -18,7 +18,7 @@
"@deepseek-ai/dsh-host-webserver": "workspace:^",
"@deepseek-ai/dsh-home-paths": "workspace:^",
"@deepseek-ai/dsh-jobs": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-tool-workspace-dependencies": "workspace:^",
"@deepseek-ai/dsh-skill-office": "workspace:^"
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
import { dirname, join } from 'node:path'
import type { Context } from '@deepseek-ai/cordis'
import * as officeSkills from '@deepseek-ai/dsh-skill-office'
import * as workspaceDependencies from './workspace-dependencies.ts'
import * as workspaceDependencies from '@deepseek-ai/dsh-tool-workspace-dependencies'
/** Loader identity for the application-owned Office composition. */
export const name = 'desktop-office'
@@ -1,54 +0,0 @@
/** Desktop tool exposing bundled interpreters without modifying command resolution. */
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
import { installPrimaryRuntime, type WorkspaceDependencies } from './primary-runtime.ts'
export const name = 'desktop-workspace-dependencies'
export const inject = ['tools']
/** Application-selected payload and installation directories. */
export interface Config {
readonly source: string
readonly root: string
}
/**
* Register the read-only path query, preparing bundled files on its first invocation.
* @param ctx - Desktop tool registry owner.
* @param config - Application payload and fixed installation paths.
*/
export function apply(ctx: Context, config: Config): void {
let installation: Promise<WorkspaceDependencies> | undefined
ctx.effect(() => async () => {
// Tool execution reports installation failures; disposal only waits for filesystem work to settle.
await installation?.catch(() => undefined)
})
ctx.tools.register(defineTool({
name: 'load_workspace_dependencies',
description: 'Get absolute paths to bundled Python, Node.js, pnpm, and library directories, plus bundled Python distribution versions. Python includes numpy, pandas, python-docx, python-pptx, openpyxl, Pillow, lxml, and XlsxWriter. Use these libraries for Office files unless the user or workspace instructions select another environment. Run pnpm with the returned Node executable and pnpm script path. This does not change PATH or package-manager settings.',
parameters: {},
output: {
schema: {
type: 'object', additionalProperties: false,
properties: {
python: { type: 'string', required: true },
node: { type: 'string', required: true },
pnpm: { type: 'string', required: true },
pythonPackages: { type: 'string', required: true },
nodePackages: { type: 'string', required: true },
pythonDistributions: { type: 'object', additionalProperties: true, required: true, description: 'Bundled distribution names and versions recorded in runtime.json; excludes user-installed additions.' },
},
},
render: (_args, value) => [{ type: 'text', text: JSON.stringify(value, undefined, 2) }],
},
execute: () => {
installation ??= installPrimaryRuntime(config.source, config.root).catch((error: unknown) => {
installation = undefined
throw error
})
return installation
},
presentCall: () => ({ card: 'generic', title: 'Load workspace dependencies', kind: 'read' }),
}))
}
+1 -1
View File
@@ -13,7 +13,7 @@
{ "path": "../../packages/jobs/jobs" },
{ "path": "../../packages/skill/skill-office" },
{ "path": "../../packages/util/home-paths" },
{ "path": "../../packages/core/tools" },
{ "path": "../../packages/skill/tool-workspace-dependencies" },
{
"path": "../cli"
},
@@ -9,7 +9,7 @@ import { tmpdir } from 'node:os'
import { dirname, join, resolve } from 'node:path'
import extractZip from 'extract-zip'
import { x as extractTar } from 'tar'
import { workspaceDependencyPaths, type PrimaryRuntimeManifest } from '../../desktop-host/src/primary-runtime.ts'
import { workspaceDependencyPaths, type PrimaryRuntimeManifest } from '../../../packages/skill/tool-workspace-dependencies/src/index.ts'
import { resolveDesktopBuildTarget, resolveDesktopTargetBuildPaths } from './desktop-build-paths.mjs'
import { scrubWindowsSigningEnvironment } from './windows-sign.mjs'
import lock from './primary-runtime-lock.json' with { type: 'json' }
@@ -159,6 +159,8 @@ export function smokePrimaryRuntime(root: string): void {
if (manifest.platform !== process.platform || manifest.arch !== process.arch) return
if (manifest.pythonPackages === undefined) throw new Error('primary runtime: missing Python distribution versions; prepare the payload before running its smoke checks.')
const entries = workspaceDependencyPaths(root, manifest)
// The Desktop payload always ships Node.js and pnpm; only other carriers may omit them.
if (entries.node === undefined || entries.pnpm === undefined) throw new Error('primary runtime: the Desktop payload must declare node and pnpm components.')
const options = { stdio: 'inherit', timeout: 120_000, env: scrubWindowsSigningEnvironment(process.env) } as const
execFileSync(entries.python, ['-I', '-c', 'import decimal, xml.parsers.expat, lzma, uuid, numpy, pandas; assert numpy.arange(4).sum() == 6; assert pandas.DataFrame({"n": [1, 2]}).n.sum() == 3'], options)
execFileSync(entries.python, ['-I', '-B', join(import.meta.dirname, 'smoke-primary-runtime.py'), JSON.stringify(manifest.pythonPackages),
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/config-catalog.md
config-catalog.md: 631ce6a34b120f44534a7935ff976662287fa879
config-catalog.zh.md: 8d72786a79ceff85e35aff72fa6622af7373a3b4
config-catalog.md: 64ef9c99d2cc98690097d848ab7cfb899c6bb474
config-catalog.zh.md: 76faa711e53011ba34d599f59f616280a4660ad0
+22
View File
@@ -3462,6 +3462,28 @@ export interface Config {
Source: [`packages/workflow/tool-workflow/src/index.ts:32`](../packages/workflow/tool-workflow/src/index.ts)
<a id="deepseek-aidsh-tool-workspace-dependencies"></a>
## `@deepseek-ai/dsh-tool-workspace-dependencies`
Requires: `tools`
```ts config-catalog
/** Payload location and optional installation directory. */
export interface Config {
/** Payload directory carrying `runtime.json` and `dependencies/`. */
readonly source: string
/**
* Installation directory under the Harness home. When set, the payload is copied there on the
* first call (the Desktop behavior); when omitted, the payload is used in place without copying,
* which suits read-only carriers such as container image layers.
*/
readonly root?: string
}
```
Source: [`packages/skill/tool-workspace-dependencies/src/index.ts:14`](../packages/skill/tool-workspace-dependencies/src/index.ts)
<a id="deepseek-aidsh-tools"></a>
## `@deepseek-ai/dsh-tools`
+22
View File
@@ -3464,6 +3464,28 @@ export interface Config {
来源:[`packages/workflow/tool-workflow/src/index.ts:32`](../packages/workflow/tool-workflow/src/index.ts)
<a id="deepseek-aidsh-tool-workspace-dependencies"></a>
## `@deepseek-ai/dsh-tool-workspace-dependencies`
依赖:`tools`
```ts config-catalog
/** Payload location and optional installation directory. */
export interface Config {
/** Payload directory carrying `runtime.json` and `dependencies/`. */
readonly source: string
/**
* Installation directory under the Harness home. When set, the payload is copied there on the
* first call (the Desktop behavior); when omitted, the payload is used in place without copying,
* which suits read-only carriers such as container image layers.
*/
readonly root?: string
}
```
来源:[`packages/skill/tool-workspace-dependencies/src/index.ts:14`](../packages/skill/tool-workspace-dependencies/src/index.ts)
<a id="deepseek-aidsh-tools"></a>
## `@deepseek-ai/dsh-tools`
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/tool-catalog.md
tool-catalog.md: cd7805d3104120a04e21363cb75c6dcb8456f5e0
tool-catalog.zh.md: 52752836f49c8fb745a69f10c4f8cd1ef79a871d
tool-catalog.md: bdc3a5af4b1bf723879c423fe201576ca3772e64
tool-catalog.zh.md: bc445ea8d6b0a5778e4bd191693ff01bd6781d73
+18
View File
@@ -43,6 +43,7 @@ This table connects model-visible tool names to the plugin package and service s
| `@deepseek-ai/dsh-experimental-tool-agent-team` | `interrupt_agent`, `list_agents`, `send_message`, `spawn_teammate`, `team_task_create`, `team_task_get`, `team_task_list`, `team_task_update`, `wait_agent` | `ctx.tools`, `ctx.systemPrompt`, `ctx.agentTeams`, `an exact live Team member Agent` | `tool/call`, `team/member`, `team/message/queued`, `team/message/delivered`, `team/task`, `tool/result` | - | All nine tools are scoped to implicit Team Leads and durable teammates. The shipped dsh-base bundle keeps the package disabled; the documented Agent Teams profile patch enables it while disabling the legacy continuable-child control names. |
| `@deepseek-ai/dsh-tool-todo` | `todo_write` | `ctx.tools`, `owning Agent session` | `tool/call`, `todo/write`, `tool/result` | - | todo_write is session-owned state; UIs render the latest todo/write event as a checklist. `allowParallelInProgress` is required with no default, so the catalog states its choice: `true`, whose description invites several `in_progress` items. A deployment choosing `false` receives the same tool with a description asking for exactly one active task. |
| `@deepseek-ai/dsh-tool-workflow` | `workflow` | `ctx.tools`, `ctx.workflowEngine`, `ctx.systemPrompt`, `a calling Agent (exec.agent parents the script children)` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-tool-workspace-dependencies` | `load_workspace_dependencies` | `ctx.tools` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-tool-web` | `web_fetch`, `web_search` | `ctx.tools`, `ctx.web`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | web_search and web_fetch keep provider selection behind ctx.web so model-visible schemas stay stable across backend swaps. |
<a id="deepseek-aidsh-plugin-manager"></a>
@@ -2438,6 +2439,23 @@ Constraints: concurrency and total-agent caps apply; no filesystem, network, tim
Source: [`packages/workflow/tool-workflow/src/index.ts`](../packages/workflow/tool-workflow/src/index.ts)
<a id="deepseek-aidsh-tool-workspace-dependencies"></a>
## `@deepseek-ai/dsh-tool-workspace-dependencies`
### `load_workspace_dependencies`
Get absolute paths to bundled Python, Node.js, pnpm, and library directories, plus bundled Python distribution versions. Python includes numpy, pandas, python-docx, python-pptx, openpyxl, Pillow, lxml, and XlsxWriter. Use these libraries for Office files unless the user or workspace instructions select another environment. Run pnpm with the returned Node executable and pnpm script path. This does not change PATH or package-manager settings.
```json
{
"type": "object",
"properties": {}
}
```
Source: [`packages/skill/tool-workspace-dependencies/src/index.ts`](../packages/skill/tool-workspace-dependencies/src/index.ts)
<a id="deepseek-aidsh-tool-web"></a>
## `@deepseek-ai/dsh-tool-web`
+18
View File
@@ -47,6 +47,7 @@
| `@deepseek-ai/dsh-experimental-tool-agent-team` | `interrupt_agent`、`list_agents`、`send_message`、`spawn_teammate`、`team_task_create`、`team_task_get`、`team_task_list`、`team_task_update`、`wait_agent` | `ctx.tools`、`ctx.systemPrompt`、`ctx.agentTeams`、`an exact live Team member Agent` | `tool/call`、`team/member`、`team/message/queued`、`team/message/delivered`、`team/task`、`tool/result` | - | 这 9 个工具限定于隐式 Team Lead 与持久 teammate 作用域。随产品发布的 dsh-base bundle 默认禁用该包;文档中的 Agent Teams profile patch 会启用它,并禁用旧 continuable child 的同名控制工具。 |
| `@deepseek-ai/dsh-tool-todo` | `todo_write` | `ctx.tools`、`owning Agent session` | `tool/call`、`todo/write`、`tool/result` | - | todo_write 是会话所有的状态;UI 将最新的 todo/write 事件渲染为检查清单。`allowParallelInProgress` 是没有默认值的必填项,因此本目录明确选择 `true`,对应描述允许同时存在多个 `in_progress` 项。选择 `false` 的部署会获得同一工具,但描述会要求只能有 1 个活动任务。 |
| `@deepseek-ai/dsh-tool-workflow` | `workflow` | `ctx.tools`、`ctx.workflowEngine`、`ctx.systemPrompt`、`a calling Agent (exec.agent parents the script children)` | `tool/call`、`tool/result` | - | - |
| `@deepseek-ai/dsh-tool-workspace-dependencies` | `load_workspace_dependencies` | `ctx.tools` | `tool/call`, `tool/result` | - | - |
| `@deepseek-ai/dsh-tool-web` | `web_fetch`、`web_search` | `ctx.tools`、`ctx.web`、`ctx.systemPrompt` | `tool/call`、`tool/result` | - | web_search 和 web_fetch 将提供方选择置于 ctx.web 之后,使模型可见 schema 在更换后端时保持稳定。 |
<a id="deepseek-aidsh-plugin-manager"></a>
@@ -2446,6 +2447,23 @@ todo_write 是会话所有的状态;UI 将最新的 todo/write 事件渲染为
来源:[`packages/workflow/tool-workflow/src/index.ts`](../packages/workflow/tool-workflow/src/index.ts)
<a id="deepseek-aidsh-tool-workspace-dependencies"></a>
## `@deepseek-ai/dsh-tool-workspace-dependencies`
### `load_workspace_dependencies`
获取随包附带的 Python、Node.js、pnpm 和库目录的绝对路径,以及随包 Python 发行版的版本。Python 含 numpy、pandas、python-docx、python-pptx、openpyxl、Pillow、lxml 与 XlsxWriter。除非用户或工作区指令选择了别的环境,Office 文件请使用这些库。用返回的 Node 可执行文件和 pnpm 脚本路径运行 pnpm。本工具不改 PATH,也不改包管理器设置。
```json
{
"type": "object",
"properties": {}
}
```
来源:[`packages/skill/tool-workspace-dependencies/src/index.ts`](../packages/skill/tool-workspace-dependencies/src/index.ts)
<a id="deepseek-aidsh-tool-web"></a>
## `@deepseek-ai/dsh-tool-web`
+2 -1
View File
@@ -240,6 +240,7 @@
"typescript": "^6.0.3",
"vite-tsconfig-paths": "^6.1.1",
"vitest": "^4.1.8",
"@deepseek-ai/dsh-skill-office": "workspace:^"
"@deepseek-ai/dsh-skill-office": "workspace:^",
"@deepseek-ai/dsh-tool-workspace-dependencies": "workspace:^"
}
}
+19
View File
@@ -23,3 +23,22 @@
- id: hmr
disabled: true
# Bundled script runtime and Office skills, the composition Desktop mounts as
# `desktop-office`. A carrier that ships the primary-runtime payload (for
# example a container image layer) names it with DSH_PRIMARY_RUNTIME; its
# sibling `office-skills/` directory carries the skill resources. Unset, both
# rows stay disabled and the profile is unchanged.
- insert:
- id: workspace-dependencies
name: '@deepseek-ai/dsh-tool-workspace-dependencies'
disabled: !!js "!process.env.DSH_PRIMARY_RUNTIME"
config:
source: !!js process.env.DSH_PRIMARY_RUNTIME ?? ''
- id: skill-office
name: '@deepseek-ai/dsh-skill-office'
disabled: !!js "!process.env.DSH_PRIMARY_RUNTIME"
config:
# The payload's parent directory plus office-skills, without a path module in the expression scope.
assetRoot: !!js "process.env.DSH_PRIMARY_RUNTIME ? process.env.DSH_PRIMARY_RUNTIME.replace(/[\\\\/]+[^\\\\/]+[\\\\/]*$/, '') + '/office-skills' : ''"
+3 -1
View File
@@ -37,7 +37,9 @@
"@deepseek-ai/dsh-cmdline": "workspace:^",
"@deepseek-ai/dsh-sdk-jsonrpc-server": "workspace:^",
"@deepseek-ai/schemastery": "workspace:^",
"commander": "^15.0.0"
"commander": "^15.0.0",
"@deepseek-ai/dsh-skill-office": "workspace:^",
"@deepseek-ai/dsh-tool-workspace-dependencies": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^"
@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/skill/tool-workspace-dependencies/README.md
README.md: d39437c039ff479900f8192e666ab67f2dbedc89
README.zh.md: c45cd21e810eb8a6a2c3e8bf22da4a5bdc8089ff
@@ -0,0 +1,122 @@
---
description: "The load_workspace_dependencies tool: absolute paths into a bundled Python, Node.js, and pnpm payload, used in place or installed under the Harness home."
kind: "package-reference"
---
# @deepseek-ai/dsh-tool-workspace-dependencies
English | [中文](README.zh.md)
## Summary
Deployments that ship their own script runtimes (Desktop's primary runtime, or a container image layer) mount this tool so the agent can ask where the bundled Python, Node.js, and pnpm live instead of discovering a system interpreter. The tool returns absolute paths and recorded distribution versions; it changes neither `PATH` nor package-manager settings. The payload is either copied under the Harness home on first use (Desktop) or used where it lies (read-only carriers).
## Table of Contents
- [Use this package](#use-this-package)
- [Understand the implementation](#understand-the-implementation)
- [Further Exploration](#further-exploration)
- [Model Experience](#model-experience)
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
- [Dev Note](#dev-note)
-----
<a id="use-this-package"></a>
## Use this package
Mount the plugin beside the tool registry with the payload directory. The bundled Office skills (`@deepseek-ai/dsh-skill-office`) reference this tool by name for their default interpreter.
### Minimal configuration
```yaml
- name: '@deepseek-ai/dsh-tool-workspace-dependencies'
config:
source: /path/to/primary-runtime
```
| Field | Default | Meaning |
|---|---|---|
| `source` | required | Payload directory carrying `runtime.json` and `dependencies/`. |
| `root` | unset | Installation directory under the Harness home. Set: the payload is copied there on the first call and reused while `runtime.json` is unchanged. Unset: the payload is validated and used in place; nothing is copied. |
### Payload layout
`runtime.json` records `desktopVersion`, `platform` (`win32`, `darwin`, or `linux`), `arch`, the `components` versions (`python`, `numpy`, `pandas`; `node` and `pnpm` are optional), and optionally `payloadDigest` and `pythonPackages`. Entries live under `dependencies/`: `python/bin/python3` (`python/python.exe` on Windows) with `site-packages` beneath it, and, when declared, `node/bin/node` with `node/node_modules` and `pnpm/bin/pnpm.mjs`. A manifest whose platform or architecture differs from the running process is rejected.
The `sdk` profile mounts this tool and the Office skills when `DSH_PRIMARY_RUNTIME` names a payload directory whose sibling `office-skills/` carries the skill resources; both rows stay disabled otherwise.
-----
<a id="understand-the-implementation"></a>
## Understand the implementation
<details>
<summary>Implementation internals — click to expand</summary>
`readPrimaryRuntime` validates the manifest and rejects duplicate normalized distribution names and numpy/pandas versions that disagree with the components table. `workspaceDependencyPaths` derives the platform-specific entries. `installPrimaryRuntime` copies into a staging directory, verifies every declared entry, and swaps it into place while retaining the previous tree on failure; `resolvePrimaryRuntime` verifies the same entries without copying. The tool memoizes the first successful preparation for the plugin's lifetime.
| File | Responsibility |
|---|---|
| [`src/index.ts`](src/index.ts) | Manifest validation, path derivation, in-place and installed preparation, tool registration. |
| — | No runtime invariant companion is published: the payload manifest is validated on every preparation, and the tool registry owns registration lifecycle. |
</details>
-----
<a id="further-exploration"></a>
## Further Exploration
- [Office skills](../skill-office/README.md) — the workflows that call this tool for their interpreter.
- [Tool registry](../../core/tools/README.md) — registration and schemas.
-----
<a id="model-experience"></a>
## Model Experience
### Tool schema
#### What the model sees
The model sees the generated [`load_workspace_dependencies` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-workspace-dependencies).
#### Token effect
Fixed schema cost per request where the tool is visible; the description names the bundled Office libraries so the model can choose the interpreter without loading a skill first.
#### KV Cache effect
Prefix-stable while the tool definition and visibility are unchanged.
### Tool result
#### What the model sees
One JSON object with absolute `python` and `pythonPackages` paths, `pythonDistributions` from `runtime.json`, and `node`, `nodePackages`, and `pnpm` when the payload declares them. Repeated calls return the same object.
#### Token effect
A few hundred characters per call; paths dominate.
#### KV Cache effect
Append-only tool result in the turn history; no prompt section is added.
## Known Limitations and Deferred Work
<a id="known-limitations-and-deferred-work"></a>
- The payload build (`apps/desktop/scripts/prepare-primary-runtime.ts`) still targets Desktop; other carriers assemble the same layout themselves.
- Windows payloads used in place must already be executable from their carrier; the in-place mode performs no permission repair.
<a id="dev-note"></a>
### Dev Note
<details>
<summary>Working context for maintainers — click to expand</summary>
Extracted from `apps/desktop-host` so the SDK profile can mount the same tool from a container image layer. The in-place mode and the optional Node.js/pnpm components exist for that carrier.
</details>
@@ -0,0 +1,122 @@
---
description: "load_workspace_dependencies 工具:返回随包附带的 Python、Node.js 与 pnpm 的绝对路径,payload 可原地使用或安装到 Harness home。"
kind: "package-reference"
---
# @deepseek-ai/dsh-tool-workspace-dependencies
[English](README.md) | 中文
## 概述
自带脚本运行时的部署(Desktop 的 primary runtime,或容器镜像层)挂载本工具,让 agent 询问随包的 Python、Node.js 与 pnpm 在哪里,而不是自行寻找系统解释器。工具返回绝对路径与记录的发行版版本,不改 `PATH`,不改包管理器设置。payload 或在首次调用时复制到 Harness home 下(Desktop),或原地使用(只读载体)。
## 目录
- [使用本包](#use-this-package)
- [了解实现](#understand-the-implementation)
- [延伸阅读](#further-exploration)
- [模型体验](#model-experience)
- [已知限制与暂缓事项](#known-limitations-and-deferred-work)
- [开发备注](#dev-note)
-----
<a id="use-this-package"></a>
## 使用本包
与工具注册表一起挂载,给出 payload 目录。内置 Office skills(`@deepseek-ai/dsh-skill-office`)按名字引用本工具取默认解释器。
### 最小配置
```yaml
- name: '@deepseek-ai/dsh-tool-workspace-dependencies'
config:
source: /path/to/primary-runtime
```
| 字段 | 默认值 | 含义 |
|---|---|---|
| `source` | 必填 | 含 `runtime.json` 与 `dependencies/` 的 payload 目录。 |
| `root` | 未设置 | Harness home 下的安装目录。设置时首次调用把 payload 复制过去,`runtime.json` 不变则复用;未设置时校验后原地使用,不复制。 |
### payload 布局
`runtime.json` 记录 `desktopVersion`、`platform`(`win32`、`darwin` 或 `linux`)、`arch`、`components` 各版本(`python`、`numpy`、`pandas`;`node` 与 `pnpm` 可选),以及可选的 `payloadDigest` 与 `pythonPackages`。条目位于 `dependencies/`:`python/bin/python3`(Windows 为 `python/python.exe`)及其下的 `site-packages`;声明了才有的 `node/bin/node`、`node/node_modules` 与 `pnpm/bin/pnpm.mjs`。平台或架构与当前进程不符的清单被拒绝。
`sdk` profile 在 `DSH_PRIMARY_RUNTIME` 指向一个 payload 目录、且其同级 `office-skills/` 带有 skill 资源时挂载本工具与 Office skills;否则两行都保持禁用。
-----
<a id="understand-the-implementation"></a>
## 了解实现
<details>
<summary>实现细节——点击展开</summary>
`readPrimaryRuntime` 校验清单,拒绝归一化后重名的发行版以及与 components 表不一致的 numpy/pandas 版本。`workspaceDependencyPaths` 推导各平台的条目路径。`installPrimaryRuntime` 复制到暂存目录、校验每个声明的条目、再换入正式位置,失败时保留旧树;`resolvePrimaryRuntime` 做同样的校验但不复制。工具在插件生命周期内记住首次成功的结果。
| 文件 | 职责 |
|---|---|
| [`src/index.ts`](src/index.ts) | 清单校验、路径推导、原地与安装两种准备方式、工具注册。 |
| — | 不发布运行时不变量伴随入口:每次准备都校验 payload 清单,工具注册表负责注册生命周期。 |
</details>
-----
<a id="further-exploration"></a>
## 延伸阅读
- [Office skills](../skill-office/README.zh.md)——调用本工具取解释器的工作流。
- [工具注册表](../../core/tools/README.zh.md)——注册与 schema。
-----
<a id="model-experience"></a>
## 模型体验
### 工具 schema
#### 模型看到什么
模型看到生成的 [`load_workspace_dependencies` schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-workspace-dependencies)。
#### Token 影响
工具可见的每个请求固定一份 schema 开销;描述里列出了随包的 Office 库,模型不必先加载 skill 就能选择解释器。
#### KV 缓存影响
工具定义与可见性不变时前缀稳定。
### 工具结果
#### 模型看到什么
一个 JSON 对象:绝对路径 `python` 与 `pythonPackages`、来自 `runtime.json` 的 `pythonDistributions`,以及 payload 声明了才有的 `node`、`nodePackages` 与 `pnpm`。重复调用返回同一对象。
#### Token 影响
每次调用几百个字符,以路径为主。
#### KV 缓存影响
作为工具结果追加进轮次历史;不增加提示词分区。
## 已知限制与暂缓事项
<a id="known-limitations-and-deferred-work"></a>
- payload 的构建脚本(`apps/desktop/scripts/prepare-primary-runtime.ts`)仍只面向 Desktop;其它载体按同一布局自行组装。
- Windows 上原地使用的 payload 须在载体里已可执行;原地模式不修复权限。
<a id="dev-note"></a>
### 开发备注
<details>
<summary>维护者的工作上下文——点击展开</summary>
从 `apps/desktop-host` 抽出,让 SDK profile 能从容器镜像层挂载同一个工具。原地模式与可选的 Node.js/pnpm 组件都是为这种载体加的。
</details>
@@ -0,0 +1,41 @@
{
"name": "@deepseek-ai/dsh-tool-workspace-dependencies",
"description": "The load_workspace_dependencies tool: absolute paths into a bundled Python, Node.js, and pnpm payload",
"version": "0.1.6-alpha.1",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/skill/tool-workspace-dependencies"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
"exports": {
".": {
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./package.json": "./package.json"
},
"files": [
"lib/index.js",
"lib/types/**/*.d.ts"
],
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/cordis-plugin-include": "workspace:^",
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^"
}
}
@@ -1,9 +1,28 @@
/** Offline installation and absolute paths for Desktop's bundled script dependencies. */
/** Model-facing query for a bundled Python, Node.js, and pnpm payload, in place or installed under the Harness home. */
import { cp, lstat, mkdir, mkdtemp, readFile, rename, rm, stat } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
/** Versions recorded by the Desktop build, independent of user-installed packages. */
/** Cordis plugin identity. */
export const name = 'tool-workspace-dependencies'
/** Registry the tool registers into. */
export const inject = ['tools']
/** Payload location and optional installation directory. */
export interface Config {
/** Payload directory carrying `runtime.json` and `dependencies/`. */
readonly source: string
/**
* Installation directory under the Harness home. When set, the payload is copied there on the
* first call (the Desktop behavior); when omitted, the payload is used in place without copying,
* which suits read-only carriers such as container image layers.
*/
readonly root?: string
}
/** Versions recorded by the payload build, independent of user-installed packages. */
export interface PrimaryRuntimeManifest {
readonly desktopVersion: string
readonly platform: string
@@ -14,8 +33,10 @@ export interface PrimaryRuntimeManifest {
readonly pythonPackages?: Readonly<Record<string, string>>
readonly components: {
readonly python: string
readonly node: string
readonly pnpm: string
/** Absent when the payload ships no Node.js. */
readonly node?: string
/** Absent when the payload ships no pnpm. */
readonly pnpm?: string
readonly numpy: string
readonly pandas: string
}
@@ -24,14 +45,21 @@ export interface PrimaryRuntimeManifest {
/** Absolute entry points and bundled versions; pnpm runs through the returned Node executable. */
export interface WorkspaceDependencies {
readonly python: string
readonly node: string
readonly pnpm: string
readonly node?: string
readonly pnpm?: string
readonly pythonPackages: string
readonly nodePackages: string
readonly nodePackages?: string
/** Locked distribution versions; excludes packages users add to the installed environment. */
readonly pythonDistributions: Readonly<Record<string, string>>
}
const VERSION = /^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/u
const PLATFORMS = ['win32', 'darwin', 'linux']
function isVersion(value: unknown): boolean {
return typeof value === 'string' && VERSION.test(value)
}
/**
* Read build metadata, rejecting duplicate normalized names and conflicting component/distribution versions.
* @param root - Installed or bundled primary runtime directory.
@@ -41,12 +69,13 @@ export async function readPrimaryRuntime(root: string): Promise<PrimaryRuntimeMa
const value: unknown = JSON.parse(await readFile(join(root, 'runtime.json'), 'utf8'))
if (typeof value !== 'object' || value === null) throw new Error('primary runtime: invalid metadata')
const record = value as Record<string, unknown>
const components = record.components
const components = record.components as Record<string, unknown> | null | undefined
const packages = record.pythonPackages
if (typeof record.desktopVersion !== 'string' || record.desktopVersion.length === 0
|| !['win32', 'darwin'].includes(String(record.platform)) || !['x64', 'arm64'].includes(String(record.arch))
|| !PLATFORMS.includes(String(record.platform)) || !['x64', 'arm64'].includes(String(record.arch))
|| typeof components !== 'object' || components === null
|| !['python', 'node', 'pnpm', 'numpy', 'pandas'].every(key => /^\d+\.\d+\.\d+(?:[-+][\w.-]+)?$/u.test(String((components as Record<string, unknown>)[key])))
|| !['python', 'numpy', 'pandas'].every(key => isVersion(components[key]))
|| !['node', 'pnpm'].every(key => components[key] === undefined || isVersion(components[key]))
|| (record.payloadDigest !== undefined && (typeof record.payloadDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(record.payloadDigest)))
|| (packages !== undefined && (typeof packages !== 'object' || packages === null || Array.isArray(packages)
|| !Object.entries(packages).every(([name, version]) => /^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(name)
@@ -68,23 +97,32 @@ export async function readPrimaryRuntime(root: string): Promise<PrimaryRuntimeMa
/**
* Resolve platform-specific interpreter and library locations without changing the environment.
* @param root - Absolute installation directory.
* @param root - Absolute payload or installation directory.
* @param manifest - Validated runtime metadata.
* @returns Absolute paths for explicit script execution and recorded bundled Python versions.
*/
export function workspaceDependencyPaths(root: string, manifest: PrimaryRuntimeManifest): WorkspaceDependencies {
const dependencies = join(root, 'dependencies')
const windows = manifest.platform === 'win32'
const node = manifest.components.node === undefined ? {} : {
node: join(dependencies, 'node', 'bin', windows ? 'node.exe' : 'node'),
nodePackages: join(dependencies, 'node', 'node_modules'),
}
const pnpm = manifest.components.pnpm === undefined ? {} : { pnpm: join(dependencies, 'pnpm', 'bin', 'pnpm.mjs') }
return {
python: join(dependencies, 'python', ...(windows ? ['python.exe'] : ['bin', 'python3'])),
node: join(dependencies, 'node', 'bin', windows ? 'node.exe' : 'node'),
pnpm: join(dependencies, 'pnpm', 'bin', 'pnpm.mjs'),
...node,
...pnpm,
pythonPackages: join(dependencies, 'python', ...(windows ? ['Lib'] : ['lib', `python${manifest.components.python.split('.').slice(0, 2).join('.')}`]), 'site-packages'),
nodePackages: join(dependencies, 'node', 'node_modules'),
pythonDistributions: manifest.pythonPackages ?? {},
}
}
function payloadEntries(paths: WorkspaceDependencies): string[] {
return [paths.python, paths.node, paths.pnpm, paths.pythonPackages, paths.nodePackages]
.filter((path): path is string => path !== undefined)
}
async function exists(path: string): Promise<boolean> {
try {
if ((await lstat(path)).isSymbolicLink()) throw new Error(`primary runtime: installation path is a filesystem link: ${path}`)
@@ -95,15 +133,31 @@ async function exists(path: string): Promise<boolean> {
}
}
async function compatibleManifest(source: string): Promise<PrimaryRuntimeManifest> {
const manifest = await readPrimaryRuntime(source)
if (manifest.platform !== process.platform || manifest.arch !== process.arch) throw new Error('primary runtime: incompatible platform or architecture')
return manifest
}
/**
* Use a payload where it lies: validate its metadata and entries, copying nothing.
* @param source - Payload directory, typically a read-only carrier.
* @returns Paths into the payload itself.
*/
export async function resolvePrimaryRuntime(source: string): Promise<WorkspaceDependencies> {
const paths = workspaceDependencyPaths(source, await compatibleManifest(source))
for (const path of payloadEntries(paths)) await stat(path)
return paths
}
/**
* Install the application-owned payload locally, retaining a complete previous tree on copy failure.
* @param source - Payload carried by the current Desktop installation.
* @param source - Payload carried by the current installation.
* @param root - Fixed primary runtime directory under the Harness home.
* @returns Paths into the installed payload; no PATH or package-manager configuration is changed.
*/
export async function installPrimaryRuntime(source: string, root: string): Promise<WorkspaceDependencies> {
const manifest = await readPrimaryRuntime(source)
if (manifest.platform !== process.platform || manifest.arch !== process.arch) throw new Error('primary runtime: incompatible platform or architecture')
const manifest = await compatibleManifest(source)
await mkdir(dirname(root), { recursive: true })
const previous = `${root}.previous`
await exists(previous)
@@ -111,14 +165,14 @@ export async function installPrimaryRuntime(source: string, root: string): Promi
if (!await exists(root) && await exists(previous)) await rename(previous, root)
if (await exists(join(root, 'runtime.json')) && JSON.stringify(await readPrimaryRuntime(root)) === JSON.stringify(manifest)) {
const paths = workspaceDependencyPaths(root, manifest)
for (const path of [paths.python, paths.node, paths.pnpm, paths.pythonPackages, paths.nodePackages]) await stat(path)
for (const path of payloadEntries(paths)) await stat(path)
return paths
}
const staging = await mkdtemp(join(dirname(root), '.primary-runtime-'))
try {
await cp(source, staging, { recursive: true, dereference: true })
const paths = workspaceDependencyPaths(staging, manifest)
for (const path of [paths.python, paths.node, paths.pnpm, paths.pythonPackages, paths.nodePackages]) await stat(path)
for (const path of payloadEntries(paths)) await stat(path)
await rm(previous, { recursive: true, force: true })
const replacing = await exists(root)
if (replacing) await rename(root, previous)
@@ -132,3 +186,44 @@ export async function installPrimaryRuntime(source: string, root: string): Promi
}
return workspaceDependencyPaths(root, manifest)
}
/**
* Register the read-only path query; the first invocation prepares (or merely validates) the payload.
* @param ctx - Tool registry owner.
* @param config - Payload location and optional installation directory.
*/
export function apply(ctx: Context, config: Config): void {
let preparation: Promise<WorkspaceDependencies> | undefined
ctx.effect(() => async () => {
// Tool execution reports preparation failures; disposal only waits for filesystem work to settle.
await preparation?.catch(() => undefined)
})
ctx.tools.register(defineTool({
name: 'load_workspace_dependencies',
description: 'Get absolute paths to bundled Python, Node.js, pnpm, and library directories, plus bundled Python distribution versions. Python includes numpy, pandas, python-docx, python-pptx, openpyxl, Pillow, lxml, and XlsxWriter. Use these libraries for Office files unless the user or workspace instructions select another environment. Run pnpm with the returned Node executable and pnpm script path. This does not change PATH or package-manager settings.',
parameters: {},
output: {
schema: {
type: 'object', additionalProperties: false,
properties: {
python: { type: 'string', required: true },
node: { type: 'string', description: 'Absent when the payload ships no Node.js.' },
pnpm: { type: 'string', description: 'Absent when the payload ships no pnpm.' },
pythonPackages: { type: 'string', required: true },
nodePackages: { type: 'string', description: 'Absent when the payload ships no Node.js.' },
pythonDistributions: { type: 'object', additionalProperties: true, required: true, description: 'Bundled distribution names and versions recorded in runtime.json; excludes user-installed additions.' },
},
},
render: (_args, value) => [{ type: 'text', text: JSON.stringify(value, undefined, 2) }],
},
execute: () => {
preparation ??= (config.root === undefined ? resolvePrimaryRuntime(config.source) : installPrimaryRuntime(config.source, config.root))
.catch((error: unknown) => {
preparation = undefined
throw error
})
return preparation
},
presentCall: () => ({ card: 'generic', title: 'Load workspace dependencies', kind: 'read' }),
}))
}
@@ -10,8 +10,8 @@ import AgentRegistry from '@deepseek-ai/dsh-agent'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRuntime from '@deepseek-ai/dsh-tools'
import { ToolCallId } from '@deepseek-ai/dsh-llm'
import { installPrimaryRuntime, readPrimaryRuntime, workspaceDependencyPaths, type PrimaryRuntimeManifest } from '../src/primary-runtime.ts'
import * as workspaceDependencies from '../src/workspace-dependencies.ts'
import * as workspaceDependencies from '../src/index.ts'
import { installPrimaryRuntime, readPrimaryRuntime, resolvePrimaryRuntime, workspaceDependencyPaths, type PrimaryRuntimeManifest } from '../src/index.ts'
const roots: string[] = []
afterEach(async () => { await Promise.all(roots.splice(0).map(root => rm(root, { recursive: true, force: true }))) })
@@ -22,22 +22,23 @@ async function fixture() {
const source = join(directory, 'resources')
const root = join(directory, 'home', 'dsh-runtimes', 'dsh-primary-runtime')
const manifest: PrimaryRuntimeManifest = {
desktopVersion: '1.0.0', platform: process.platform === 'win32' ? 'win32' : 'darwin', arch: process.arch,
desktopVersion: '1.0.0', platform: process.platform, arch: process.arch,
components: { python: '3.12.14', node: '24.21.0', pnpm: '11.7.0', numpy: '2.3.5', pandas: '3.0.1' },
pythonPackages: { 'python-docx': '1.2.0', 'python-pptx': '1.0.2', openpyxl: '3.1.5' },
}
const paths = workspaceDependencyPaths(source, manifest)
for (const path of [paths.python, paths.node, paths.pnpm]) {
if (path === undefined) continue
await mkdir(dirname(path), { recursive: true })
await writeFile(path, 'interpreter')
}
await mkdir(paths.pythonPackages, { recursive: true })
await mkdir(paths.nodePackages, { recursive: true })
if (paths.nodePackages !== undefined) await mkdir(paths.nodePackages, { recursive: true })
await writeFile(join(source, 'runtime.json'), JSON.stringify(manifest))
return { source, root, manifest, directory }
}
it.each(['win32', 'darwin'])('returns %s interpreter and package paths', (platform) => {
it.each(['win32', 'darwin', 'linux'])('returns %s interpreter and package paths', (platform) => {
const manifest: PrimaryRuntimeManifest = { desktopVersion: '1', platform, arch: 'x64', components: { python: '3.12.14', node: '24.21.0', pnpm: '11.7.0', numpy: '2.3.5', pandas: '3.0.1' } }
const paths = workspaceDependencyPaths('/runtime', manifest)
expect(paths.pythonDistributions).toEqual({})
@@ -153,8 +154,8 @@ it.each([
await expect(readPrimaryRuntime(source)).rejects.toThrow('invalid metadata')
})
it.skipIf(process.platform === 'linux')('loads the real tool through Cordis, exposes installed paths, and unregisters on disposal', async () => {
const { source, root, manifest, directory } = await fixture()
it('loads the real tool through Cordis, exposes payload paths in place, and unregisters on disposal', async () => {
const { source, manifest, directory } = await fixture()
const ctx = new Context()
try {
ctx.baseUrl = pathToFileURL(directory).href + '/'
@@ -171,7 +172,7 @@ it.skipIf(process.platform === 'linux')('loads the real tool through Cordis, exp
},
} as unknown as NonNullable<typeof ctx.loader.internal>
const config = join(directory, 'cordis.yml')
await writeFile(config, `- name: agents\n- name: systemPrompt\n- name: tools\n- name: dependencies\n config:\n source: ${JSON.stringify(source)}\n root: ${JSON.stringify(root)}\n`)
await writeFile(config, `- name: agents\n- name: systemPrompt\n- name: tools\n- name: dependencies\n config:\n source: ${JSON.stringify(source)}\n`)
await ctx.loader.create({ name: 'cordis:include', config: { path: pathToFileURL(config).href } })
await ctx.loader.await()
for (const entry of ctx.loader.entries()) await entry.fiber?.await()
@@ -180,7 +181,7 @@ it.skipIf(process.platform === 'linux')('loads the real tool through Cordis, exp
const results = await Promise.all(['first', 'second'].map(id => ctx.tools.execute({ ...request, callId: ToolCallId(id) })))
for (const result of results) {
expect(result.isError).toBe(false)
expect(result.content).toEqual([{ type: 'text', text: JSON.stringify(workspaceDependencyPaths(root, manifest), undefined, 2) }])
expect(result.content).toEqual([{ type: 'text', text: JSON.stringify(workspaceDependencyPaths(source, manifest), undefined, 2) }])
}
expect(process.env).toEqual(environment)
const entry = [...ctx.loader.entries()].find(entry => entry.options.name === 'dependencies')
@@ -191,3 +192,28 @@ it.skipIf(process.platform === 'linux')('loads the real tool through Cordis, exp
await ctx.fiber.dispose()
}
})
it('uses a payload in place without copying and tolerates a payload without Node.js or pnpm', async () => {
const { source, directory } = await fixture()
const manifest: PrimaryRuntimeManifest = {
desktopVersion: '1.0.0', platform: process.platform, arch: process.arch,
components: { python: '3.12.14', numpy: '2.3.5', pandas: '3.0.1' },
pythonPackages: { 'python-docx': '1.2.0' },
}
await writeFile(join(source, 'runtime.json'), JSON.stringify(manifest))
const paths = await resolvePrimaryRuntime(source)
expect(paths).toEqual({
python: join(source, 'dependencies', 'python', ...(process.platform === 'win32' ? ['python.exe'] : ['bin', 'python3'])),
pythonPackages: join(source, 'dependencies', 'python', ...(process.platform === 'win32' ? ['Lib'] : ['lib', 'python3.12']), 'site-packages'),
pythonDistributions: { 'python-docx': '1.2.0' },
})
await expect(readFile(join(directory, 'home', 'dsh-runtimes', 'dsh-primary-runtime', 'runtime.json'))).rejects.toMatchObject({ code: 'ENOENT' })
await rm(paths.python)
await expect(resolvePrimaryRuntime(source)).rejects.toMatchObject({ code: 'ENOENT' })
})
it('rejects a payload whose Node.js or pnpm version is malformed', async () => {
const { source, manifest } = await fixture()
await writeFile(join(source, 'runtime.json'), JSON.stringify({ ...manifest, components: { ...manifest.components, node: 'latest' } }))
await expect(readPrimaryRuntime(source)).rejects.toThrow('invalid metadata')
})
@@ -0,0 +1,21 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib/types"
},
"include": [
"src"
],
"references": [
{
"path": "../../../vendor/cosmokit"
},
{
"path": "../../../vendor/cordis"
},
{
"path": "../../core/tools"
}
]
}
+47 -2
View File
@@ -54,6 +54,9 @@ importers:
'@deepseek-ai/dsh-tool-session-query':
specifier: workspace:^
version: link:packages/session-query/tool-session-query
'@deepseek-ai/dsh-tool-workspace-dependencies':
specifier: workspace:^
version: link:packages/skill/tool-workspace-dependencies
'@deepseek-ai/dsh-web-fetch-http':
specifier: workspace:^
version: link:packages/web/web-fetch-http
@@ -297,6 +300,9 @@ importers:
'@deepseek-ai/dsh-skill-filesystem':
specifier: workspace:^
version: link:../../packages/skill/skill-filesystem
'@deepseek-ai/dsh-skill-office':
specifier: workspace:^
version: link:../../packages/skill/skill-office
'@deepseek-ai/dsh-terminal':
specifier: workspace:^
version: link:../../packages/terminal/terminal
@@ -369,6 +375,9 @@ importers:
'@deepseek-ai/dsh-tool-workflow':
specifier: workspace:^
version: link:../../packages/workflow/tool-workflow
'@deepseek-ai/dsh-tool-workspace-dependencies':
specifier: workspace:^
version: link:../../packages/skill/tool-workspace-dependencies
'@deepseek-ai/dsh-web-app':
specifier: workspace:^
version: link:../../packages/bundle/web-app
@@ -617,9 +626,9 @@ importers:
'@deepseek-ai/dsh-skill-office':
specifier: workspace:^
version: link:../../packages/skill/skill-office
'@deepseek-ai/dsh-tools':
'@deepseek-ai/dsh-tool-workspace-dependencies':
specifier: workspace:^
version: link:../../packages/core/tools
version: link:../../packages/skill/tool-workspace-dependencies
apps/web:
devDependencies:
@@ -1942,6 +1951,12 @@ importers:
'@deepseek-ai/dsh-sdk-jsonrpc-server':
specifier: workspace:^
version: link:../../sdk/server
'@deepseek-ai/dsh-skill-office':
specifier: workspace:^
version: link:../../skill/skill-office
'@deepseek-ai/dsh-tool-workspace-dependencies':
specifier: workspace:^
version: link:../../skill/tool-workspace-dependencies
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
@@ -10299,6 +10314,30 @@ importers:
specifier: workspace:^
version: link:../../core/tools
packages/skill/tool-workspace-dependencies:
devDependencies:
'@deepseek-ai/cordis':
specifier: workspace:^
version: link:../../../vendor/cordis
'@deepseek-ai/cordis-plugin-include':
specifier: workspace:^
version: link:../../../vendor/include
'@deepseek-ai/cordis-plugin-loader':
specifier: workspace:^
version: link:../../../vendor/loader
'@deepseek-ai/dsh-agent':
specifier: workspace:^
version: link:../../core/agent
'@deepseek-ai/dsh-llm':
specifier: workspace:^
version: link:../../llm/llm
'@deepseek-ai/dsh-system-prompt':
specifier: workspace:^
version: link:../../core/system-prompt
'@deepseek-ai/dsh-tools':
specifier: workspace:^
version: link:../../core/tools
packages/spill/spill:
devDependencies:
'@deepseek-ai/cordis':
@@ -12615,6 +12654,9 @@ importers:
'@deepseek-ai/dsh-skill-filesystem':
specifier: workspace:^
version: link:../../packages/skill/skill-filesystem
'@deepseek-ai/dsh-skill-office':
specifier: workspace:^
version: link:../../packages/skill/skill-office
'@deepseek-ai/dsh-spill':
specifier: workspace:^
version: link:../../packages/spill/spill
@@ -12714,6 +12756,9 @@ importers:
'@deepseek-ai/dsh-tool-workflow':
specifier: workspace:^
version: link:../../packages/workflow/tool-workflow
'@deepseek-ai/dsh-tool-workspace-dependencies':
specifier: workspace:^
version: link:../../packages/skill/tool-workspace-dependencies
'@deepseek-ai/dsh-tools':
specifier: workspace:^
version: link:../../packages/core/tools
+2
View File
@@ -86,6 +86,8 @@
"@deepseek-ai/dsh-shell-env": "workspace:^",
"@deepseek-ai/dsh-skill": "workspace:^",
"@deepseek-ai/dsh-skill-filesystem": "workspace:^",
"@deepseek-ai/dsh-skill-office": "workspace:^",
"@deepseek-ai/dsh-tool-workspace-dependencies": "workspace:^",
"@deepseek-ai/dsh-spill": "workspace:^",
"@deepseek-ai/dsh-subagent": "workspace:^",
"@deepseek-ai/dsh-subagent-acp": "workspace:^",
+12
View File
@@ -75,6 +75,7 @@ import WorkflowEngine from '@deepseek-ai/dsh-workflow'
import type { WorkflowRun, WorkflowStartRequest } from '@deepseek-ai/dsh-workflow'
import * as ToolRalph from '@deepseek-ai/dsh-tool-ralph'
import * as ToolWorkflow from '@deepseek-ai/dsh-tool-workflow'
import * as ToolWorkspaceDependencies from '@deepseek-ai/dsh-tool-workspace-dependencies'
import { githubSlug } from './verify-md-links.ts'
/** Attachment seam marker that makes the attachments-conditional `read_image` schema harvestable. */
@@ -637,6 +638,17 @@ const TOOL_PACKAGES: ToolPackage[] = [
await ctx.plugin(ToolWorkflow)
},
},
{
pkg: '@deepseek-ai/dsh-tool-workspace-dependencies',
dir: 'tool-workspace-dependencies',
source: 'packages/skill/tool-workspace-dependencies/src/index.ts',
requires: ['ctx.tools'],
writes: ['tool/call', 'tool/result'],
async mount(ctx) {
// Schema harvest never prepares a payload; the directory need not exist.
await ctx.plugin(ToolWorkspaceDependencies, { source: resolve(root, '.tmp/tool-catalog/primary-runtime') })
},
},
{
pkg: '@deepseek-ai/dsh-tool-web',
dir: 'tool-web',
@@ -46,7 +46,7 @@
- insert:
- id: workspace-dependencies
name: ../../../apps/desktop-host/src/workspace-dependencies.ts
name: '@deepseek-ai/dsh-tool-workspace-dependencies'
config:
source: !!js process.cwd() + "/runtime-source"
root: !!js process.cwd() + "/runtime-installed"
@@ -1,6 +1,6 @@
- insert:
- id: workspace-dependencies
name: ../../../apps/desktop-host/src/workspace-dependencies.ts
name: '@deepseek-ai/dsh-tool-workspace-dependencies'
config:
source: !!js process.cwd() + '/runtime-source'
root: !!js process.cwd() + '/runtime-installed'
+1
View File
@@ -481,6 +481,7 @@
"@deepseek-ai/dsh-tool-web": ["./packages/web/tool-web/src"],
"@deepseek-ai/dsh-tool-workflow": ["./packages/workflow/tool-workflow/src"],
"@deepseek-ai/dsh-tool-workflow/invariant": ["./packages/workflow/tool-workflow/src/invariant.ts"],
"@deepseek-ai/dsh-tool-workspace-dependencies": ["./packages/skill/tool-workspace-dependencies/src"],
"@deepseek-ai/dsh-tools": ["./packages/core/tools/src"],
"@deepseek-ai/dsh-tools/invariant": ["./packages/core/tools/src/invariant.ts"],
"@deepseek-ai/dsh-user-approval": ["./packages/interaction/user-approval/src"],
+1
View File
@@ -161,6 +161,7 @@
"references": [
{ "path": "./packages/client/connection/tsconfig.host.json" },
{ "path": "./packages/skill/skill-office" },
{ "path": "./packages/skill/tool-workspace-dependencies" },
{ "path": "./packages/browser-use/browser-use" },
{ "path": "./packages/experimental/browser-use-runtime" },
{ "path": "./packages/experimental/browser-use-playwright-mcp" },