Merge remote-tracking branch 'origin/master' into feat/read-image-context

# Conflicts:
#	apps/cli/tests/web-agent-presets.e2e.ts
#	packages/fs/tool-fs/package.json
This commit is contained in:
creatixchu
2026-08-11 10:12:14 +08:00
1962 changed files with 25048 additions and 14905 deletions

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 packages/README.md
README.md: c246534a26cd7297f2ba8885099c8b517a5dc2b0
README.zh.md: 4139874d3ebbf82fad8680a3721a1cf35a553706
README.md: 19d6e5ba7b554f59bd66e213f8a53389761fc735
README.zh.md: 17f58a2922e9019af054b0dccb6c4d9199fd1a9d

View File

@@ -38,7 +38,7 @@ Groups hold `packages/<group>/<pkg>/`; names stay `@deepseek-ai/dsh-<pkg>`. **Gr
| [`preset/`](preset/README.md) | Per-session agent composition from preset `cordis.yml` files | Product — stable surface |
| [`guard/`](guard/README.md) | Loop-hygiene guards: advisory repeat-call reminders + the `tools/execute` deadline enforcer | Product — stable surface |
| [`bundle/`](bundle/README.md) | Installable `dsh --profile` patch layers | Product — stable surface |
| [`self-modification/`](self-modification/README.md) | Agent runtime self-modification: live plugin/service inspection, model-written plugin mount/unmount ([design](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md)), restricted repository Plugin loading | Product — stable surface |
| [`self-modification/`](self-modification/README.md) | Agent runtime self-modification: live plugin/service inspection and model-written plugin mount/unmount ([design](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md)) | Product — stable surface |
| [`hooks/`](hooks/README.md) | Hook bridges + the shared Claude Code / Codex wire-protocol library | Product — stable surface |
| [`session/`](session/README.md) | Durable session data plane: persistence seam + JSONL/SQLite backends, projection seam, log-backed titles, session reporting | Product — stable surface |
| [`session-query/`](session-query/README.md) | Session retrieval family: logical corpus, bounded reads, lineage, event relationships, semantic filtering, and SQLite full-text search | Product — stable surface |

View File

@@ -38,7 +38,7 @@ npm scope 为 `@deepseek-ai/dsh-*`Cordis `Service` 子类和函数插件通
| [`preset/`](preset/README.md) | 由 preset `cordis.yml` 按会话组装 agent | 产品:稳定接口 |
| [`guard/`](guard/README.md) | 循环卫生守卫:建议性重复调用提醒 + `tools/execute` 截止时间强制执行器 | 产品:稳定接口 |
| [`bundle/`](bundle/README.md) | 可安装的 `dsh --profile` 补丁层 | 产品:稳定接口 |
| [`self-modification/`](self-modification/README.md) | agent 运行时自修改:实时插件/服务检查模型所写插件挂载/卸载([设计](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md)、受限仓库插件加载 | 产品:稳定接口 |
| [`self-modification/`](self-modification/README.md) | agent 运行时自修改:实时插件/服务检查模型所写插件挂载/卸载([设计](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md) | 产品:稳定接口 |
| [`hooks/`](hooks/README.md) | 钩子桥接 + 共享 Claude CodeCodex 协议格式库 | 产品:稳定接口 |
| [`session/`](session/README.md) | 持久会话数据平面:持久化 seam + JSONL/SQLite 后端、投影 seam、日志支持的标题、会话上报 | 产品:稳定接口 |
| [`session-query/`](session-query/README.md) | 会话检索系列:逻辑语料库、有界读取、血缘、事件关系、语义过滤和 SQLite 全文搜索 | 产品:稳定接口 |

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-acp",
"description": "Automation-only Agent Client Protocol server for driving DeepSeek Harness agents over JSON-RPC stdio",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/acp/acp"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -26,14 +33,14 @@
"license": "BSD-3-Clause",
"dependencies": {
"@agentclientprotocol/sdk": "0.25.1",
"schemastery": "^3.17.0"
"@deepseek-ai/schemastery": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/dsh-agent": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-session": "^0.0.1",
"@deepseek-ai/dsh-user-approval": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
@@ -44,6 +51,6 @@
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -9,11 +9,11 @@
* @module @deepseek-ai/dsh-acp
*/
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import { randomUUID } from 'node:crypto'
import { isAbsolute } from 'node:path'
import { Readable, Writable } from 'node:stream'
import Schema from 'schemastery'
import Schema from '@deepseek-ai/schemastery'
import { createUserMessage, errorChain } from '@deepseek-ai/dsh-llm'
import {
AgentSideConnection,

View File

@@ -4,7 +4,7 @@
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-acp'

View File

@@ -1,6 +1,6 @@
/** In-memory ACP transport fixture over the real agent factory and loop. */
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import {
ClientSideConnection,
ndJsonStream,

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-api-gateway",
"description": "TypeRT Remote Host dispatcher and Client API endpoint",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/api/gateway"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -26,13 +33,15 @@
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"dshClient": {
"inject": [
"@deepseek-ai/dsh-typert-registry",
"@deepseek-ai/dsh-client-connection"
],
"platform": "web",
"immediately": true
"dsh": {
"client": {
"inject": [
"@deepseek-ai/dsh-typert-registry",
"@deepseek-ai/dsh-client-connection"
],
"platform": "web",
"immediately": true
}
},
"scripts": {
"bundle": "tsdown",
@@ -50,17 +59,17 @@
"@deepseek-ai/dsh-type-meta": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/dsh-client-connection": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-typert-registry": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-typert-registry": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-host-webserver": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-typert-registry": "workspace:^",
"cordis": "^4.0.0-rc.7",
"@deepseek-ai/cordis": "workspace:^",
"zod": "^4.4.3"
}
}

View File

@@ -4,8 +4,8 @@
* participates in method lookup, invocation, or type exposure.
*/
import { Service } from 'cordis'
import type { Context } from 'cordis'
import { Service } from '@deepseek-ai/cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { ConnectionHandle, RpcError } from '@deepseek-ai/dsh-client-connection/client'
import type {
InvocationDescriptor,
@@ -53,7 +53,7 @@ interface RemoteNamespaceHandle {
/** Typed Remote service augmented by generated direct namespaces. */
export type ClientRemote = TypeRTClientRemote
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
/** Generated Remote namespaces selected by the Client assembly. */
remote: ClientRemote

View File

@@ -4,7 +4,7 @@
* @module @deepseek-ai/dsh-api-gateway
*/
import { Context, Service, symbols } from 'cordis'
import { Context, Service, symbols } from '@deepseek-ai/cordis'
import type { ConnectionRpcHandler } from '@deepseek-ai/dsh-client-connection'
import {
remoteMethods,

View File

@@ -4,7 +4,7 @@
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-api-gateway'

View File

@@ -46,7 +46,7 @@ export interface TypertGateway {
invoke(request: InvokeRemoteRequest): Promise<unknown>
}
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
/** Host dispatcher for TypeRT Remote calls. */
typertGateway: TypertGateway

View File

@@ -1,4 +1,4 @@
import { Context, Service } from 'cordis'
import { Context, Service } from '@deepseek-ai/cordis'
import { describe, expect, it, vi } from 'vitest'
import { z } from 'zod'
import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client'

View File

@@ -1,7 +1,7 @@
import { createServer } from 'node:http'
import type { AddressInfo } from 'node:net'
import { describe, expect, it } from 'vitest'
import { Context, Service, symbols } from 'cordis'
import { Context, Service, symbols } from '@deepseek-ai/cordis'
import { z } from 'zod'
import { apply as applyConnection, inject as connectionInject } from '@deepseek-ai/dsh-client-connection'
import type { HttpServerService, WebRoute } from '@deepseek-ai/dsh-host-webserver'

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-api-remotes",
"description": "Remote BFF assembly and Host Agent/Session lookup policy",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/api/remotes"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -22,12 +29,14 @@
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"dshClient": {
"inject": [
"@deepseek-ai/dsh-api-gateway"
],
"platform": "web",
"immediately": true
"dsh": {
"client": {
"inject": [
"@deepseek-ai/dsh-api-gateway"
],
"platform": "web",
"immediately": true
}
},
"scripts": {
"bundle": "tsdown",
@@ -44,13 +53,13 @@
"@deepseek-ai/dsh-type-meta": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/dsh-agent": "^0.0.1",
"@deepseek-ai/dsh-goal": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-session": "^0.0.1",
"@deepseek-ai/dsh-session-persistence": "^0.0.1",
"@deepseek-ai/dsh-typert-registry": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-goal": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-persistence": "workspace:^",
"@deepseek-ai/dsh-typert-registry": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
@@ -59,6 +68,6 @@
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-persistence": "workspace:^",
"@deepseek-ai/dsh-typert-registry": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -1,6 +1,6 @@
/** Host BFF policy for resolving Remote Agent and Session identities. */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { Agent, AgentOptions, AgentSetup } from '@deepseek-ai/dsh-agent'
import type { Session, SessionEvent, SessionHeader, SessionId } from '@deepseek-ai/dsh-session'
import type {} from '@deepseek-ai/dsh-session-persistence'

View File

@@ -1,13 +1,13 @@
/** Platform-neutral assembly of generated Host Remote contributions. */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import goalsRemote from '@deepseek-ai/dsh-goal/remote'
import type { TypeRTClientRemote } from '@deepseek-ai/dsh-type-meta'
export type { TypeRTClientRemote as ClientRemote } from '@deepseek-ai/dsh-type-meta'
export type {} from '@deepseek-ai/dsh-goal/remote'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
/** Generated Remote namespaces selected by this Client assembly. */
remote: TypeRTClientRemote

View File

@@ -1,7 +1,7 @@
/** Package-owned invariant companion for `@deepseek-ai/dsh-api-remotes`. */
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-api-remotes'

View File

@@ -1,5 +1,5 @@
import { describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import AgentRegistry from '@deepseek-ai/dsh-agent'
import type { Agent } from '@deepseek-ai/dsh-agent'
import SessionStore from '@deepseek-ai/dsh-session'

View File

@@ -45,7 +45,7 @@ describe.skipIf(!requiredArtifacts)('Goal Remote built LIB chain', () => {
}).map(([key, path]) => [key, artifactUrl(path)]))
const script = `
import { createServer } from 'node:http'
import * as cordis from 'cordis'
import * as cordis from '@deepseek-ai/cordis'
const urls = ${JSON.stringify(urls)}
const { Context } = cordis
@@ -123,7 +123,7 @@ describe.skipIf(!requiredArtifacts)('Goal Remote built LIB chain', () => {
const handoff = handoffs.get(id)
if (handoff === undefined) throw new Error('missing Client bundle handoff ' + id)
return handoff.factory(specifier => {
if (specifier === 'cordis') return cordis
if (specifier === '@deepseek-ai/cordis') return cordis
throw new Error('unexpected Client external ' + specifier)
})
}

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-attachment-local",
"description": "Private content-addressed DSH_HOME attachment storage",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/attachment/attachment-local"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -15,19 +22,19 @@
"files": ["lib/index.js", "lib/invariant.js", "lib/types/**/*.d.ts"],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-attachment": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-paths": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-attachment": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-paths": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"schemastery": "^3.18.0",
"@deepseek-ai/schemastery": "workspace:^",
"sharp": "^0.35.3"
},
"devDependencies": {
"@deepseek-ai/dsh-attachment": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-paths": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -1,8 +1,8 @@
/** Local durable attachment backend rooted below `DSH_HOME`. @module @deepseek-ai/dsh-attachment-local */
import { join, resolve } from 'node:path'
import { Context } from 'cordis'
import z from 'schemastery'
import { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
import { AttachmentStore } from '@deepseek-ai/dsh-attachment'
import type { ImageAttachmentLimits, ImageAttachmentRef, SaveImageAttachment, StoredImageAttachment } from '@deepseek-ai/dsh-attachment'
import { resolveDshHome } from '@deepseek-ai/dsh-paths'

View File

@@ -1,7 +1,7 @@
/** Package-owned invariant companion for `@deepseek-ai/dsh-attachment-local`. @module @deepseek-ai/dsh-attachment-local/invariant */
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-attachment-local'

View File

@@ -1,4 +1,4 @@
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { existsSync } from 'node:fs'
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-attachment",
"description": "Durable immutable attachment storage seam for the DeepSeek Harness",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/attachment/attachment"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -15,13 +22,13 @@
"files": ["lib/index.js", "lib/invariant.js", "lib/types/**/*.d.ts"],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-brand": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -1,6 +1,6 @@
/** Durable attachment storage seam (`ctx.attachments`). @module @deepseek-ai/dsh-attachment */
import { Context, Service } from 'cordis'
import { Context, Service } from '@deepseek-ai/cordis'
import type {
ImageAttachmentLimits,
ImageAttachmentRef,
@@ -19,7 +19,7 @@ export type {
StoredImageAttachment,
} from './types.ts'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
attachments: AttachmentStore
}

View File

@@ -1,7 +1,7 @@
/** Package-owned invariant companion for `@deepseek-ai/dsh-attachment`. @module @deepseek-ai/dsh-attachment/invariant */
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-attachment'

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 packages/bash/bash-env/README.md
README.md: 7b939326d4effd14fc83ef0ad4e133f019f1011f
README.zh.md: 4d80d9d34f2be18e07d57d2427eb841f61f1ccfc
README.md: 54758a91773aad723c6fbbebe2ffe25aedb38599
README.zh.md: e0663b003e94942c76cde4fb7867ed0ce74c2aa2

View File

@@ -22,7 +22,7 @@ Every foreground and background model shell call receives a newly collected trus
`ctx.bashEnv` owns collection. Other plugins can register an effect-scoped contributor with a stable name, declared keys/descriptions, and `resolve(execution: ToolExecution)`; duplicate ownership and undeclared runtime keys fail loudly, while `list()` enumerates declarations without executing providers. Harness built-ins reserve `DSH_HOME`, `DSH_SHELL`, and `DSH_SESSION_ID`; this plugin's persistence translator owns `DSH_SESSION_JSONL` by reading the backend-neutral `sessionPersistence.locate()` seam.
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type {} from '@deepseek-ai/dsh-bash-env'
export const inject = ['bashEnv']

View File

@@ -22,7 +22,7 @@
`ctx.bashEnv` 负责收集。其他插件可以注册一个受 effect 作用域约束的 contributor带有稳定名称、已声明的键/描述以及 `resolve(execution: ToolExecution)`;重复所有权与未声明的运行时键会响亮失败,而 `list()` 只枚举声明、不执行 provider。Harness 内置键保留 `DSH_HOME``DSH_SHELL``DSH_SESSION_ID`;本插件的持久化翻译器通过读取与后端无关的 `sessionPersistence.locate()` seam 拥有 `DSH_SESSION_JSONL`
```ts
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type {} from '@deepseek-ai/dsh-bash-env'
export const inject = ['bashEnv']

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-bash-env",
"description": "Tool-independent managed DSH_* shell environment registry",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/bash/bash-env"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -25,15 +32,15 @@
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-bash": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-paths": "^0.0.1",
"@deepseek-ai/dsh-session-persistence": "^0.0.1",
"@deepseek-ai/dsh-tools": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-bash": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-paths": "workspace:^",
"@deepseek-ai/dsh-session-persistence": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"schemastery": "^3.18.0"
"@deepseek-ai/schemastery": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
@@ -43,6 +50,6 @@
"@deepseek-ai/dsh-paths": "workspace:^",
"@deepseek-ai/dsh-session-persistence": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -8,15 +8,15 @@
* @module @deepseek-ai/dsh-bash-env
*/
import { Service, type Context } from 'cordis'
import z from 'schemastery'
import { Service, type Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
import { DSH_ENV_PREFIX } from '@deepseek-ai/dsh-bash'
import type { DshEnvironment, DshEnvironmentKey } from '@deepseek-ai/dsh-bash'
import { DSH_HOME_ENV, resolveDshHome } from '@deepseek-ai/dsh-paths'
import type { ToolExecution } from '@deepseek-ai/dsh-tools'
import type {} from '@deepseek-ai/dsh-session-persistence'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
bashEnv: BashEnvRegistry
}

View File

@@ -4,7 +4,7 @@
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-bash-env'

View File

@@ -7,7 +7,7 @@
import { homedir } from 'node:os'
import { join, resolve } from 'node:path'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { CallId } from '@deepseek-ai/dsh-llm'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type { ToolExecution } from '@deepseek-ai/dsh-tools'

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-bash-local",
"description": "Local-subprocess implementation of the DeepSeek Harness bash executor seam",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/bash/bash-local"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -25,14 +32,14 @@
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-bash": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-subprocess": "^0.0.1",
"@deepseek-ai/dsh-timeout": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-bash": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-subprocess": "workspace:^",
"@deepseek-ai/dsh-timeout": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"schemastery": "^3.18.0"
"@deepseek-ai/schemastery": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-bash": "workspace:^",
@@ -40,6 +47,6 @@
"@deepseek-ai/dsh-subprocess": "workspace:^",
"@deepseek-ai/dsh-subprocess-local": "workspace:^",
"@deepseek-ai/dsh-timeout": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -9,8 +9,8 @@
* @module @deepseek-ai/dsh-bash-local
*/
import { Context } from 'cordis'
import z from 'schemastery'
import { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
import { BashExecutor } from '@deepseek-ai/dsh-bash'
import type { BashExecRequest, BashExecSpec, BashProcess, BashProcessRead, BashRunResult, CollectedOutput } from '@deepseek-ai/dsh-bash'
import type { SubprocessCollect, SubprocessHandle, SubprocessOutputReader, SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess'

View File

@@ -4,7 +4,7 @@
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-bash-local'

View File

@@ -2,7 +2,7 @@ import { mkdtempSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local'
import LocalSubprocessService from '@deepseek-ai/dsh-subprocess-local'
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-bash-sandbox",
"description": "Sandbox-consuming implementation of the DeepSeek Harness bash executor seam (confines every command via ctx.sandbox, reports denial/enforcement result facts)",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/bash/bash-sandbox"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -25,12 +32,12 @@
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-bash": "^0.0.1",
"@deepseek-ai/dsh-bash-local": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-sandbox": "^0.0.1",
"@deepseek-ai/dsh-sandbox-policy": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-bash": "workspace:^",
"@deepseek-ai/dsh-bash-local": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-sandbox": "workspace:^",
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-bash": "workspace:^",
@@ -40,7 +47,7 @@
"@deepseek-ai/dsh-sandbox": "workspace:^",
"@deepseek-ai/dsh-sandbox-local": "workspace:^",
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
"cordis": "^4.0.0-rc.7",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/node-addon-landlock-run": "workspace:*"
}
}

View File

@@ -8,7 +8,7 @@
* @module @deepseek-ai/dsh-bash-sandbox
*/
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import type { BashExecRequest, BashExecSpec, BashProcess, BashRunResult } from '@deepseek-ai/dsh-bash'
import { SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
import type {

View File

@@ -4,7 +4,7 @@
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-bash-sandbox'

View File

@@ -4,7 +4,7 @@ import { mkdtemp, rm } from 'node:fs/promises'
import { homedir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
import { bwrapProfileArgs } from '@deepseek-ai/dsh-sandbox-local/src/profiles.ts'

View File

@@ -4,7 +4,7 @@ import { mkdtemp, rm } from 'node:fs/promises'
import { homedir, tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { launcherPath } from '@deepseek-ai/node-addon-landlock-run'
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'

View File

@@ -8,7 +8,7 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { LAUNCHER_FAILURE_EXIT } from '@deepseek-ai/node-addon-landlock-run'
import { SANDBOX_UNAVAILABLE, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'

View File

@@ -9,7 +9,7 @@ import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
import { describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import type { BashRunResult, CollectedOutput } from '@deepseek-ai/dsh-bash'
import { SANDBOX_UNAVAILABLE, SandboxProvider, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
import type { ConfinedArgv, SandboxExecutionPolicy, SandboxMode, SandboxPolicy } from '@deepseek-ai/dsh-sandbox'

View File

@@ -4,7 +4,7 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { homedir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
import { seatbeltProfileArgs } from '@deepseek-ai/dsh-sandbox-local/src/profiles.ts'

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-bash",
"description": "Abstract bash executor seam (ctx.bash) for the DeepSeek Harness",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/bash/bash"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -25,15 +32,15 @@
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-subprocess": "^0.0.1",
"@deepseek-ai/dsh-sandbox": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-subprocess": "workspace:^",
"@deepseek-ai/dsh-sandbox": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-subprocess": "workspace:^",
"@deepseek-ai/dsh-sandbox": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -5,7 +5,7 @@
* @module @deepseek-ai/dsh-bash
*/
import { Context, Service } from 'cordis'
import { Context, Service } from '@deepseek-ai/cordis'
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
import type { BashExecRequest, BashExecSpec, BashProcess, BashRunResult } from './types.ts'
@@ -25,7 +25,7 @@ export type {
export { parseExitStatus } from './render.ts'
export type { ParsedExitStatus } from './render.ts'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
bash: BashExecutor
}

View File

@@ -1,6 +1,6 @@
/** Package-owned invariant companion for the bash seam. @module @deepseek-ai/dsh-bash/invariant */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-bash'

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { BashExecutor } from '@deepseek-ai/dsh-bash'
import type { BashExecRequest, BashExecSpec, BashProcess, BashProcessRead, BashRunResult } from '@deepseek-ai/dsh-bash'

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-pwsh-local",
"description": "Local PowerShell implementation of the DeepSeek Harness bash executor seam",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/bash/pwsh-local"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -25,14 +32,14 @@
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-bash": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-subprocess": "^0.0.1",
"@deepseek-ai/dsh-timeout": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-bash": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-subprocess": "workspace:^",
"@deepseek-ai/dsh-timeout": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"schemastery": "^3.18.0"
"@deepseek-ai/schemastery": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-bash": "workspace:^",
@@ -40,6 +47,6 @@
"@deepseek-ai/dsh-subprocess": "workspace:^",
"@deepseek-ai/dsh-subprocess-local": "workspace:^",
"@deepseek-ai/dsh-timeout": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -13,8 +13,8 @@
* @module @deepseek-ai/dsh-pwsh-local
*/
import { Context } from 'cordis'
import z from 'schemastery'
import { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
import { BashExecutor } from '@deepseek-ai/dsh-bash'
import type { BashExecRequest, BashExecSpec, BashProcess, BashProcessRead, BashRunResult, CollectedOutput } from '@deepseek-ai/dsh-bash'
import type { SubprocessCollect, SubprocessHandle, SubprocessOutputReader, SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess'

View File

@@ -4,7 +4,7 @@
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-pwsh-local'

View File

@@ -14,7 +14,7 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { spawnSync } from 'node:child_process'
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { PwshLocalExecutor, ENCODING_PREAMBLE, candidatePwshPaths, resolvePwshPath } from '@deepseek-ai/dsh-pwsh-local'
import LocalSubprocessService from '@deepseek-ai/dsh-subprocess-local'
import SubprocessService from '@deepseek-ai/dsh-subprocess'

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-pwsh-sandbox",
"description": "Sandbox-consuming implementation of the DeepSeek Harness PowerShell executor seam (confines every command via ctx.sandbox, reports denial/enforcement result facts)",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/bash/pwsh-sandbox"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -25,12 +32,12 @@
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-bash": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-pwsh-local": "^0.0.1",
"@deepseek-ai/dsh-sandbox": "^0.0.1",
"@deepseek-ai/dsh-sandbox-policy": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-bash": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-pwsh-local": "workspace:^",
"@deepseek-ai/dsh-sandbox": "workspace:^",
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-bash": "workspace:^",
@@ -40,6 +47,6 @@
"@deepseek-ai/dsh-sandbox-local": "workspace:^",
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
"@deepseek-ai/dsh-subprocess-local": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -12,7 +12,7 @@
* @module @deepseek-ai/dsh-pwsh-sandbox
*/
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import type { BashExecRequest, BashExecSpec, BashProcess, BashRunResult } from '@deepseek-ai/dsh-bash'
import { SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
import type {

View File

@@ -4,7 +4,7 @@
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-pwsh-sandbox'

View File

@@ -12,7 +12,7 @@ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:
import { homedir, tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import type { SandboxExecutionPolicy } from '@deepseek-ai/dsh-sandbox'
import { resolvePwshPath } from '@deepseek-ai/dsh-pwsh-local'
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'

View File

@@ -11,7 +11,7 @@ import { chmodSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterAll, describe, expect, it } from 'vitest'
import { Context, Service } from 'cordis'
import { Context, Service } from '@deepseek-ai/cordis'
import { SandboxProvider, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
import type { ConfinedArgv, RunnerFailureRule, SandboxExecutionPolicy, SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
import { resolvePwshPath } from '@deepseek-ai/dsh-pwsh-local'

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-tool-bash",
"description": "Model-facing bash tool with optional generic background-task and sandbox-escalation support",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/bash/tool-bash"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -25,21 +32,21 @@
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-agent": "^0.0.1",
"@deepseek-ai/dsh-bash": "^0.0.1",
"@deepseek-ai/dsh-bash-env": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-llm": "^0.0.1",
"@deepseek-ai/dsh-sandbox": "^0.0.1",
"@deepseek-ai/dsh-sandbox-policy": "^0.0.1",
"@deepseek-ai/dsh-system-prompt": "^0.0.1",
"@deepseek-ai/dsh-tasks": "^0.0.1",
"@deepseek-ai/dsh-tools": "^0.0.1",
"@deepseek-ai/dsh-user-approval": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-bash": "workspace:^",
"@deepseek-ai/dsh-bash-env": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-sandbox": "workspace:^",
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-tasks": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"schemastery": "^3.18.0"
"@deepseek-ai/schemastery": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
@@ -61,6 +68,6 @@
"@deepseek-ai/dsh-tool-tasks": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -8,8 +8,8 @@
* @module @deepseek-ai/dsh-tool-bash
*/
import type { Context } from 'cordis'
import z from 'schemastery'
import type { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
import { isAbsolute, resolve as resolvePath } from 'node:path'
import { defineTool, TOOL_ABORTED } from '@deepseek-ai/dsh-tools'
import type { GenericCallView, TerminalCallView, ToolExecution, ToolResult, ToolResultView } from '@deepseek-ai/dsh-tools'

View File

@@ -4,7 +4,7 @@
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-tool-bash'

View File

@@ -1,6 +1,6 @@
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'

View File

@@ -2,7 +2,7 @@ import { mkdtempSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { CallId } from '@deepseek-ai/dsh-llm'
import { BashExecutor } from '@deepseek-ai/dsh-bash'
import type { BashExecRequest, BashExecSpec, BashProcess, BashProcessRead, BashRunResult } from '@deepseek-ai/dsh-bash'

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-tool-pwsh",
"description": "Model-facing pwsh tool over the bash executor seam",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/bash/tool-pwsh"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -25,21 +32,21 @@
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-agent": "^0.0.1",
"@deepseek-ai/dsh-bash": "^0.0.1",
"@deepseek-ai/dsh-bash-env": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-llm": "^0.0.1",
"@deepseek-ai/dsh-sandbox": "^0.0.1",
"@deepseek-ai/dsh-sandbox-policy": "^0.0.1",
"@deepseek-ai/dsh-system-prompt": "^0.0.1",
"@deepseek-ai/dsh-tasks": "^0.0.1",
"@deepseek-ai/dsh-tools": "^0.0.1",
"@deepseek-ai/dsh-user-approval": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-bash": "workspace:^",
"@deepseek-ai/dsh-bash-env": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-sandbox": "workspace:^",
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-tasks": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"schemastery": "^3.18.0"
"@deepseek-ai/schemastery": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
@@ -58,6 +65,6 @@
"@deepseek-ai/dsh-tool-tasks": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -20,8 +20,8 @@
*/
import { isAbsolute, resolve as resolvePath } from 'node:path'
import type { Context } from 'cordis'
import z from 'schemastery'
import type { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
import { defineTool, TOOL_ABORTED } from '@deepseek-ai/dsh-tools'
import type { GenericCallView, TerminalCallView, ToolExecution, ToolResult, ToolResultView } from '@deepseek-ai/dsh-tools'
import { HarnessError } from '@deepseek-ai/dsh-llm'

View File

@@ -4,7 +4,7 @@
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-tool-pwsh'

View File

@@ -14,7 +14,7 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { spawnSync } from 'node:child_process'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { CallId } from '@deepseek-ai/dsh-llm'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, { TOOL_ABORTED } from '@deepseek-ai/dsh-tools'

View File

@@ -11,7 +11,7 @@
*/
import { describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import { mkdtempSync, realpathSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join, resolve as resolvePath } from 'node:path'

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 packages/boot/README.md
README.md: 5e4e483b60adab0b22ddb5279f4cd8fb699b9c35
README.zh.md: 95a3f98129a7d1fdfaffb3cac6fed77bab7cff56
README.md: 79d653260ea4a9d9a4c71a593b41a6a7e17efa14
README.zh.md: 839be164328ef168cd6ac18bf2f1dcb930dfce3e

View File

@@ -7,5 +7,6 @@ The channel-neutral boot library the app bins share: `apps/cli`, the [`scaffold/
| Package | Role | ctx key |
|---|---|---|
| `app-boot/` | Shared boot glue for the app bins: `.env` loading, fail-loud Loader guards, snapshot-aware config resolution, the settle-the-tree boot sequence | (library for the bins) |
| `cmdline/` | Launcher-to-app command-line handoff and app-owned startup parsing | `cmdlineArgs`, `appExit` |
The boot sequence and personal-config contract are documented in [`app-boot/README.md`](app-boot/README.md).
The boot sequence and personal-config contract are documented in [`app-boot/README.md`](app-boot/README.md); app-owned command lines are documented in [`cmdline/README.md`](cmdline/README.md).

View File

@@ -7,5 +7,6 @@
| 包 | 职责 | ctx 键 |
|---|---|---|
| `app-boot/` | app bin 的共享启动粘合层:加载 `.env`、会明确报错的 Loader 保护机制、感知快照的配置解析,以及等待整棵树停稳的启动序列 | (供各 bin 使用的库) |
| `cmdline/` | 启动器到应用的命令行交接,以及由应用持有的启动解析 | `cmdlineArgs``appExit` |
启动序列与个人配置约定见 [`app-boot/README.md`](app-boot/README.md)。
启动序列与个人配置约定见 [`app-boot/README.md`](app-boot/README.md);由应用持有的命令行见 [`cmdline/README.md`](cmdline/README.md)

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 packages/boot/app-boot/README.md
README.md: be03bceb39935fafb7acc7d3a99c1fe3af686f94
README.zh.md: 10165486712fc078cdf1f4147522397a15c88955
README.md: 1d56b2b6d22c08574f8e361955bee1dbe2aca601
README.zh.md: 5429a1322d0311f03c7c43946753a290e28cd936

View File

@@ -15,10 +15,10 @@ Shared boot glue for the app bins ([`dsh`](../../../apps/cli/README.md) and [`ds
| `assertEntriesActivated(ctx, binName)` | Include the `assertEntriesLoaded` check, then await every enabled entry after the Loader settles; throw with each failed plugin's original stack or each pending plugin's unresolved services |
| `loadOptionalPatches(binName, file)` | Parse an optional patch-list file (a profile's `cordis.patch.yml`) — a top-level YAML array of include `PatchOptions` (id-targeted config overrides, `insert` lists, `!!js` allowed); absent file → `undefined`, an unreadable/unparsable/non-array file throws |
| `loadOverlayPatches(binName, file)` | Parse a required top-level YAML array containing the same include `PatchOptions` entries described above; a missing file also throws because the caller named it |
| `mountRootInclude(ctx, absoluteConfigPath, patches?)` | Register the statically imported `cordis:include` and `cordis:group` builtins, mount the include, and retain the exact root entry used by user patch-layer HMR |
| `mountRootInclude(ctx, absoluteConfigPath, patches?, bareModuleBaseUrl?)` | Register the statically imported `cordis:include` and `cordis:group` builtins, mount the include, and retain the exact root entry used by user patch-layer HMR; an optional module base anchors bare package names to the installed host while relative names stay config-relative |
| `watchUserPatches(ctx, options)` | Register the named patch file with the existing Cordis HMR service; each add/change/removal transactionally recomposes the full patch list through the caller's `compose` closure (app-owned layers around the current user layer) and returns an async disposer |
| `resolveProfileDir` / `initProfile` / `loadProfile` / `readProfileManifest` / `writeProfileManifest` / `resolveBundleDir` / `composeEntries` / `healProfilesModuleFallback` / `PROFILE_TEMPLATES` / `DEFAULT_PROFILE_BUNDLES` / `PROFILES_DIR` / `PROFILE_PATCH_FILENAME` | Profile machinery (see [Profiles](#profiles)) |
| `boot(binName, absoluteConfigPath, patches?, prepare?)` | Create the root context, expose `dshHomePath(...segments)` to Loader `!!js` config expressions, install Loader, run optional host preparation before config-tree entries mount (`prepare` may use Loader and provide launcher-owned context slots), then mount and await the include tree, assert entries loaded and activated, and return the root context — or dispose the partial context and reject a labelled error |
| `boot(binName, absoluteConfigPath, patches?, prepare?, bareModuleBaseUrl?)` | Create the root context, expose `dshHomePath(...segments)` to Loader `!!js` config expressions, install Loader, run optional host preparation before config-tree entries mount (`prepare` may use Loader and provide launcher-owned context slots), then mount and await the include tree, assert entries loaded and activated, and return the root context — or dispose the partial context and reject a labelled error; the optional module base has the same resolution semantics as `mountRootInclude` |
| `renderConfigDump(binName, absoluteConfigPath, layers, warn?)` | Compose the base config and labeled overlay layers offline with the include's own parser and patch algorithm (`entryListSchema`/`applyEntryPatches`), so the result equals what `boot()` mounts, and render YAML with `!!js` expressions verbatim; each run of rows that shares one source file and the same patch layers is preceded by a `# ==` comment naming that file and those layers, keeping the output one loadable document; a patch matching no row goes to `warn` with its layer label (default: one stderr line), and read, parse, or field validation failures throw |
| `addHarnessSourceSection(ctx, sourceRoot)` | Add a global `harness:source` prompt section (ordered just after the harness identity, before the persona) telling the agent the on-disk path to the DSH implementation checkout while warning it not to infer the current working directory from that path and to use `pwd` instead; a no-op returning `undefined` when the booted tree has no `systemPrompt` service. The section is registered against that service's fiber, so a dev HMR reload of the system prompt drops it until the next boot |
| `HARNESS_SOURCE_SECTION` | The `'harness:source'` section name `addHarnessSourceSection` registers under |
@@ -29,7 +29,7 @@ The Loader mounts entries concurrently, so a surface can already own the termina
`cordis:group` is registered beside `cordis:include` so a composition can give one `isolate` realm to a provider and its consumers together. Both load through the ambient module pipeline rather than the included tree's own specifier resolution, which is what lets a composition outside this workspace — an agent preset under the Harness home — use a group row at all.
Bare plugin specifiers in a config (`@deepseek-ai/dsh-*`, npm packages) resolve through the Cordis Loader's internal module loader. Repository bins install Loader's optional `node-addon-require-builtin` peer; external callers must supply it or install plugins where plain Node import resolution can find them. Relative specifiers resolve against the config directory without the native helper. The built `dsh-app-boot` artifact embeds the statically mounted Include implementation while leaving Loader external, so the include tree and host bind to one Loader peer. The `dsh` source launcher additionally maps manifest-declared workspace packages to their TypeScript source; its configuration gate requires every shipped raw/Web bare plugin to appear in the resolver manifest's `dependencies`.
Bare plugin specifiers in a config (`@deepseek-ai/dsh-*`, npm packages) resolve through the Cordis Loader's internal module loader. They resolve from the config directory by default; a closed runtime passes `bareModuleBaseUrl` to `boot` or `mountRootInclude` so its installed package tree remains authoritative even when the config lives inside another Node project. Relative specifiers always resolve against the config directory. Repository bins install Loader's optional `node-addon-require-builtin` peer; external callers must supply it or install plugins where plain Node import resolution can find them. The built `dsh-app-boot` artifact embeds the statically mounted Include implementation while leaving Loader external, so the include tree and host bind to one Loader peer. The `dsh` source launcher additionally maps manifest-declared workspace packages to their TypeScript source; its configuration gate requires every shipped raw/Web bare plugin to appear in the resolver manifest's `dependencies`.
This package carries no loader hooks and no dev-mode surface. The [`dsh` app](../../../apps/cli/README.md) owns its Node source-launch hook and consumes these helpers for the boot sequence; built consumers continue to use plain Node package resolution.
@@ -42,7 +42,7 @@ User-level machine-local preferences also live in the Harness home:
- **`.env`** — the product CLI's ordinary environment layers: the invoking directory's file outranks the Harness-home file, and both sit below the inherited environment. `loadLayeredEnv` snapshots each value's source, rejects [bootstrap-only file variables](../../../.agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.md#decision) case-insensitively, and materializes accepted values into `process.env` for Loader expressions and third-party libraries. Managed credentials live separately in [`.credentials.yaml`](../../credentials/credentials-local/README.md); a credential left in either `.env` remains a lower-priority fallback.
- **`cordis.patch.yml`** (home level) and **`profiles/<name>/cordis.patch.yml`** — the user patch layers, applied after every bundle layer (per-profile first, then the home-level file, which therefore outranks it): an id-targeted patch replaces the named entry's whole `config` (restate unchanged fields), `insert` adds entries, and `!!js` expressions interpolate at mount. A patch naming an entry id absent from the composed tree is a stderr warning. An empty or comments-only file throws (it parses to nothing, not to a list); disable the layer with `[]`.
Long-lived surfaces keep `cordis.patch.yml` live through `watchUserPatches`; one-shot runs read only the startup value. The watcher targets the exact path even when the file or immediate parent does not exist, serializes bursts, and recomposes the user patches inside the caller's layer order (bundle layers below, overlay/flag patches above). A rejected read, parse, or Loader candidate leaves the last good tree running and the HMR service broadcasts `hmr/config-update-failed(filename, Error)` after logging it; observer failures are contained. Disposing the context closes the watcher and drains an active refresh.
Long-lived surfaces keep `cordis.patch.yml` live through `watchUserPatches`; one-shot runs read only the startup value. The watcher targets the exact path even when the file or immediate parent does not exist, serializes bursts, and recomposes the user patches inside the caller's layer order (bundle layers below, overlays above). A rejected read, parse, or Loader candidate leaves the last good tree running and the HMR service broadcasts `hmr/config-update-failed(filename, Error)` after logging it; observer failures are contained. Disposing the context closes the watcher and drains an active refresh.
## Model Experience

View File

@@ -15,10 +15,10 @@
| `assertEntriesActivated(ctx, binName)` | 先执行 `assertEntriesLoaded` 检查,再在 Loader 结算后等待每个已启用配置项;抛出的错误包含每个失败插件的原始错误堆栈,或每个等待中插件尚未解析的服务 |
| `loadOptionalPatches(binName, file)` | 解析一份可选的 patch 列表文件(即 profile 的 `cordis.patch.yml`):其顶层是一个 YAML 数组,内容为 include 的 `PatchOptions`(按 id 定位的配置覆盖、`insert` 列表,允许 `!!js`);文件不存在时返回 `undefined`,文件不可读、不可解析或内容不是数组时抛出异常 |
| `loadOverlayPatches(binName, file)` | 解析必需的顶层 YAML 数组,其中包含与上文相同的 include `PatchOptions` 条目;文件缺失也会抛出异常,因为该文件是调用方指名的 |
| `mountRootInclude(ctx, absoluteConfigPath, patches?)` | 注册静态导入的 `cordis:include``cordis:group` builtin挂载 include并保留用户 patch 层 HMR热模块替换使用的确切根配置项 |
| `mountRootInclude(ctx, absoluteConfigPath, patches?, bareModuleBaseUrl?)` | 注册静态导入的 `cordis:include``cordis:group` builtin挂载 include并保留用户 patch 层 HMR热模块替换使用的确切根配置项;可选模块基准会把裸包名锚定到已安装宿主,而相对名称仍以配置目录为基准 |
| `watchUserPatches(ctx, options)` | 向现有 Cordis HMR 服务注册指名的 patch 文件;每次新增、变更或移除都会通过调用方的 `compose` 闭包(应用自有层围绕当前用户层)以事务方式重新组合完整 patch 列表,并返回异步清理函数 |
| `resolveProfileDir` / `initProfile` / `loadProfile` / `readProfileManifest` / `writeProfileManifest` / `resolveBundleDir` / `composeEntries` / `healProfilesModuleFallback` / `PROFILE_TEMPLATES` / `DEFAULT_PROFILE_BUNDLES` / `PROFILES_DIR` / `PROFILE_PATCH_FILENAME` | Profile 机制(见 [Profile](#profiles) |
| `boot(binName, absoluteConfigPath, patches?, prepare?)` | 创建根上下文,向 Loader `!!js` 配置表达式暴露 `dshHomePath(...segments)` 并安装 Loader在配置树条目挂载前执行可选的宿主准备操作`prepare` 可以使用 Loader也可以提供由启动器拥有的上下文插槽再挂载并等待 include 树结算,断言所有条目均已加载并激活,最后返回根上下文——失败时 dispose资源释放部分构造的上下文并以带标签的错误 reject |
| `boot(binName, absoluteConfigPath, patches?, prepare?, bareModuleBaseUrl?)` | 创建根上下文,向 Loader `!!js` 配置表达式暴露 `dshHomePath(...segments)` 并安装 Loader在配置树条目挂载前执行可选的宿主准备操作`prepare` 可以使用 Loader也可以提供由启动器拥有的上下文插槽再挂载并等待 include 树结算,断言所有条目均已加载并激活,最后返回根上下文——失败时 dispose资源释放部分构造的上下文并以带标签的错误 reject;可选模块基准与 `mountRootInclude` 的解析语义相同 |
| `renderConfigDump(binName, absoluteConfigPath, layers, warn?)` | 使用 include 自己的解析器和补丁算法(`entryListSchema`/`applyEntryPatches`)离线合成基础配置与带标签的覆盖层,使结果与 `boot()` 挂载的内容一致,再渲染为 YAML并原样保留 `!!js` 表达式;每段来源于同一文件且由相同补丁层修改的连续行之前都有一条 `# ==` 注释,标明该文件和这些补丁层,输出仍是一份可加载的文档;未匹配到行的补丁连同其层标签交给 `warn`(默认:一行 stderr读取、解析或字段验证失败则抛出 |
| `addHarnessSourceSection(ctx, sourceRoot)` | 添加全局 `harness:source` 提示词段落(顺序紧随 harness 身份、位于 persona 之前),告知 agent智能体DSH 实现代码 checkout 的磁盘路径,同时提醒它不得据此推断当前工作目录,而应使用 `pwd`;如果已启动树没有此项服务,则不执行操作并返回 `undefined`。这里的服务是 `systemPrompt`;该段落注册到它的 fiber因此开发环境 HMR热模块替换重新加载系统提示词后它会消失直至下次启动 |
| `HARNESS_SOURCE_SECTION` | `'harness:source'` 段落名称,供 `addHarnessSourceSection` 注册使用 |
@@ -29,7 +29,7 @@ Loader 并发挂载各个条目,因此当其他环节失败时,某个界面
`cordis:group``cordis:include` 一并注册,使一份组装能把一个提供方与它的消费方放进同一个 `isolate` realm。两者都通过宿主的模块管线加载而非被包含树自身的说明符解析这正是让本工作区之外的组装——放在 Harness home 下的 agent preset——能够使用 group 行的原因。
配置中的裸插件 specifier`@deepseek-ai/dsh-*`、npm 包)通过 Cordis Loader 的内部模块 loader 解析。仓库 bin 会安装 Loader 的可选 peer `node-addon-require-builtin`;外部调用方必须提供该组件,或者把插件安装到普通 Node import 解析可以找到的位置。相对 specifier 无需原生 helper并以配置目录为基准解析。构建后的 `dsh-app-boot` 产物内嵌静态挂载的 Include 实现,但仍将 Loader 保持为外部依赖,因此 include 树与宿主会绑定到同一个 Loader peer。`dsh` 源码启动器还会将 manifest元数据清单声明的 workspace 包映射到其 TypeScript 源码其配置门禁要求每个随附的原始Web 裸插件都出现在解析所用 manifest 的 `dependencies` 中。
配置中的裸插件 specifier`@deepseek-ai/dsh-*`、npm 包)通过 Cordis Loader 的内部模块 loader 解析。默认情况下,它们从配置目录解析;封闭运行时会向 `boot``mountRootInclude` 传入 `bareModuleBaseUrl`,使已安装包树保持权威,即使配置位于另一个 Node 项目中也不受遮蔽。相对 specifier 始终以配置目录为基准解析。仓库 bin 会安装 Loader 的可选 peer `node-addon-require-builtin`;外部调用方必须提供该组件,或者把插件安装到普通 Node import 解析可以找到的位置。构建后的 `dsh-app-boot` 产物内嵌静态挂载的 Include 实现,但仍将 Loader 保持为外部依赖,因此 include 树与宿主会绑定到同一个 Loader peer。`dsh` 源码启动器还会将 manifest元数据清单声明的 workspace 包映射到其 TypeScript 源码其配置门禁要求每个随附的原始Web 裸插件都出现在解析所用 manifest 的 `dependencies` 中。
此包不包含 loader 钩子,也不提供开发模式接口。[`dsh` 应用](../../../apps/cli/README.md) 持有自己的 Node 源码启动钩子,并在启动序列中使用这些 helper构建后的消费方仍使用普通 Node 包解析。
@@ -42,7 +42,7 @@ profile 是位于 `$DSH_HOME/profiles/<name>` 下的目录Harness home 由 [`
- **`.env`**:产品 CLI 的普通环境层;调用目录的文件优先于 Harness home 的文件,两者都低于继承环境。`loadLayeredEnv` 记录每个值的来源,按不区分大小写的方式拒绝 [bootstrap-only 文件变量](../../../.agents/notes/implemented/architecture/2026-08-04-configuration-source-ownership.md#decision),并把其余值物化进 `process.env`,供 Loader 表达式和第三方库使用。受管凭据另存于 [`.credentials.yaml`](../../credentials/credentials-local/README.md);留在任一 `.env` 中的凭据仍是低优先级后备值。
- **`cordis.patch.yml`**home 级)与 **`profiles/<name>/cordis.patch.yml`**:用户 patch 层,应用在所有组合包层之后(先应用逐 profile 的文件,再应用 home 级文件,因此后者优先级更高):按 id 定位的 patch 会替换对应条目的整个 `config`(未改字段也要重述),`insert` 会添加条目,`!!js` 表达式则在挂载时插值。如果 patch 指定的条目 id 不在组合后的树中,则输出一条 stderr 警告。空文件或仅含注释的文件会抛出异常(其解析结果为空,而不是列表);如需禁用该层,请使用 `[]`
长期运行的界面会持续应用 `cordis.patch.yml` 的变更,具体由 `watchUserPatches` 负责;一次性运行只读取启动时的值。即使该文件或其直接父目录不存在,监视器仍会监视确切路径;它会串行处理突发变更,并按调用方的层次顺序重新组合用户 patch组合包层在下、overlay/标志 patch 在上)。读取失败、解析失败或 Loader 候选被拒时最后一个可用树会继续运行HMR 服务记录错误后广播 `hmr/config-update-failed(filename, Error)`,并隔离观察方的失败。上下文 dispose 时会关闭 watcher并等待进行中的刷新结束。
长期运行的界面会持续应用 `cordis.patch.yml` 的变更,具体由 `watchUserPatches` 负责;一次性运行只读取启动时的值。即使该文件或其直接父目录不存在,监视器仍会监视确切路径;它会串行处理突发变更,并按调用方的层次顺序重新组合用户 patch组合包层在下、overlay 在上)。读取失败、解析失败或 Loader 候选被拒时最后一个可用树会继续运行HMR 服务记录错误后广播 `hmr/config-update-failed(filename, Error)`,并隔离观察方的失败。上下文 dispose 时会关闭 watcher并等待进行中的刷新结束。
## 模型体验

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-app-boot",
"description": "Shared boot glue for the app bins: .env loading, fail-loud Loader guards, snapshot-aware config resolution, and the Loader boot sequence",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/boot/app-boot"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -28,32 +35,32 @@
"js-yaml": "^4.2.0"
},
"peerDependencies": {
"@cordisjs/plugin-group": "^1.0.0",
"@cordisjs/plugin-hmr": "^1.0.15",
"@cordisjs/plugin-include": "^1.0.4",
"@cordisjs/plugin-loader": "^1.0.0-rc.5",
"@deepseek-ai/dsh-environment": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-paths": "^0.0.1",
"@deepseek-ai/dsh-system-prompt": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis-plugin-group": "workspace:^",
"@deepseek-ai/cordis-plugin-hmr": "workspace:^",
"@deepseek-ai/cordis-plugin-include": "workspace:^",
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/dsh-environment": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-paths": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"peerDependenciesMeta": {
"@cordisjs/plugin-hmr": {
"@deepseek-ai/cordis-plugin-hmr": {
"optional": true
}
},
"devDependencies": {
"@cordisjs/plugin-group": "workspace:^",
"@cordisjs/plugin-hmr": "workspace:^",
"@cordisjs/plugin-include": "workspace:^",
"@cordisjs/plugin-loader": "workspace:^",
"@cordisjs/plugin-timer": "workspace:^",
"@deepseek-ai/cordis-plugin-group": "workspace:^",
"@deepseek-ai/cordis-plugin-hmr": "workspace:^",
"@deepseek-ai/cordis-plugin-include": "workspace:^",
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/cordis-plugin-timer": "workspace:^",
"@deepseek-ai/dsh-environment": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-paths": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@types/js-yaml": "^4.0.9",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -9,19 +9,19 @@
import { pathToFileURL } from 'node:url'
import { readFileSync } from 'node:fs'
import { parseEnv } from 'node:util'
import { basename, dirname, resolve } from 'node:path'
import { basename, dirname, isAbsolute, resolve } from 'node:path'
import * as yaml from 'js-yaml'
import { Context, type FiberState } from 'cordis'
import Loader, { type Entry, type EntryOptions } from '@cordisjs/plugin-loader'
import Include, { applyEntryPatches, entryListSchema, type PatchOptions } from '@cordisjs/plugin-include'
import Group from '@cordisjs/plugin-group'
import { Context, type FiberState } from '@deepseek-ai/cordis'
import Loader, { type Entry, type EntryOptions } from '@deepseek-ai/cordis-plugin-loader'
import Include, { applyEntryPatches, entryListSchema, type PatchOptions } from '@deepseek-ai/cordis-plugin-include'
import Group from '@deepseek-ai/cordis-plugin-group'
import { dshHomePath, resolveDshHome } from '@deepseek-ai/dsh-paths'
import { createEnvironmentSnapshot, type EnvironmentSnapshot } from '@deepseek-ai/dsh-environment'
import type {} from '@cordisjs/plugin-hmr'
import type {} from '@deepseek-ai/cordis-plugin-hmr'
// Side-effect type import: resolves `ctx.get('systemPrompt')` to the service.
import type {} from '@deepseek-ai/dsh-system-prompt'
declare module 'cordis' {
declare module '@deepseek-ai/cordis' {
interface Context {
/** Harness-home path resolver available to Loader `!!js` config expressions. */
dshHomePath?: typeof dshHomePath
@@ -199,7 +199,7 @@ export function loadLayeredEnv(
const bootstrapIncludes = new WeakMap<Context, Entry>()
// The include's YAML dialect (`!!js` scalars become expression nodes the
// Loader interpolates against each entry's context at mount time), imported
// Loader interpolates against each entry's injection-ready context), imported
// from the include itself so patch parsing and config dumping can never drift
// from what the include mounts. User patch layers share it so they may
// reference `process.env`.
@@ -215,7 +215,7 @@ export interface UserPatchWatchOptions {
* Compose the full patch list for a fresh user-layer generation —
* the same composition the app booted with, so a reload can interleave the
* new user patches between app-owned layers (bundle layers below,
* overlay/flag patches above). Identity when omitted: the user layer
* overlays above). Identity when omitted: the user layer
* is the whole patch list.
*/
compose?: (userPatches: PatchOptions[]) => PatchOptions[]
@@ -265,7 +265,7 @@ export async function watchUserPatches(
/**
* Load an optional patch-list file: a top-level YAML array of loader patch
* entries (`@cordisjs/plugin-include`'s `PatchOptions`): id-targeted config
* entries (`@deepseek-ai/cordis-plugin-include`'s `PatchOptions`): id-targeted config
* overrides and `insert` lists, with `!!js` expressions allowed. A missing
* file means "no layer"; an unreadable, unparsable, or non-array file throws —
* a present patch file that cannot apply is a misconfiguration and must fail
@@ -305,7 +305,7 @@ export function loadOverlayPatches(binName: string, file: string): PatchOptions[
}
/**
* Parse one loader patch list: a top-level YAML array of
* `@cordisjs/plugin-include` `PatchOptions` (id-targeted config overrides and
* `@deepseek-ai/cordis-plugin-include` `PatchOptions` (id-targeted config overrides and
* `insert` lists, `!!js` expressions allowed). Every invalid field or value throws,
* because a patch file that cannot be applied at all is a misconfiguration; a
* single patch whose target row is absent stays a per-entry Loader warning, so
@@ -476,6 +476,8 @@ function groupedDump(
* @param ctx - context carrying an initialized Loader service.
* @param absoluteConfigPath - absolute YAML or JSON configuration path.
* @param patches - initial app and user patches, applied in order.
* @param bareModuleBaseUrl - optional installed-host base for bare package
* names; relative names continue to resolve beside the configuration file.
* @returns the created root Include entry, or `undefined` when a surface
* disposed the whole tree (taking the Loader service with it) while the
* transactional create was still settling entry lifecycle.
@@ -484,24 +486,38 @@ export async function mountRootInclude(
ctx: Context,
absoluteConfigPath: string,
patches: readonly PatchOptions[] = [],
bareModuleBaseUrl?: string,
): Promise<Entry | undefined> {
ctx.loader.builtins.include = Include
ctx.loader.builtins.include = bareModuleBaseUrl === undefined
? Include
: class HostResolvedRootInclude extends Include {
override import(name: string, getOuterStack?: () => string[]): unknown {
const specifier = isAbsolute(name) ? pathToFileURL(name).href : name
if (name.startsWith('.') || name.startsWith('cordis:')) return super.import(specifier, getOuterStack)
const internal = this.ctx.loader.internal
/* v8 ignore next -- Node supplies the internal loader; this preserves the
original diagnostic for hypothetical embedders without it. */
if (internal === undefined) return super.import(specifier, getOuterStack)
return internal.import(specifier, bareModuleBaseUrl, {})
}
}
// `cordis:group` alongside it: a group row is how a composition gives one
// `isolate` realm to a provider and its consumers together, and an agent
// preset living outside this workspace cannot resolve `@cordisjs/plugin-group`
// preset living outside this workspace cannot resolve `@deepseek-ai/cordis-plugin-group`
// by name. Both builtins load through the ambient module pipeline, so neither
// depends on the included tree's own specifier resolution.
ctx.loader.builtins.group = Group
// Pinned id: the bootstrap include is app glue, not a config row, and its
// id appears in Loader failure chains — a random id would make startup
// diagnostics unstable across runs (and snapshot fixtures).
const includeConfig: Include.Config = {
path: pathToFileURL(absoluteConfigPath).href,
...patches.length > 0 ? { patches: [...patches] } : {},
}
const rootInclude: EntryOptions = {
id: 'include',
name: 'cordis:include',
config: {
path: pathToFileURL(absoluteConfigPath).href,
...patches.length > 0 ? { patches: [...patches] } : {},
},
config: includeConfig,
}
const includeId = await ctx.loader.create(rootInclude)
const loader = ctx.get('loader')
@@ -709,14 +725,13 @@ export async function assertEntriesActivated(ctx: Context, binName: string): Pro
/**
* Boot the Loader against `absoluteConfigPath` and return only after the whole
* tree settles. Entry names load through the Loader's internal module loader
* against `baseUrl` (the config directory), which may live outside
* `node_modules` reach and, unbuilt, cannot load vendored source; the
* bootstrap include is therefore statically imported and mounted as the
* `cordis:include` builtin, loading through the ambient module pipeline
* (vite/tsx/plain ESM) while the included tree's own specifiers stay
* config-relative. The package build embeds Include while leaving Loader
* external, so the built include tree and host share one Loader peer. Loader
* tree settles. Relative entry names resolve against the config directory;
* bare package names resolve there by default or against an explicit
* `bareModuleBaseUrl` for closed packaged runtimes. The bootstrap include
* is statically imported and mounted as the `cordis:include` builtin, loading
* through the ambient module pipeline (vite/tsx/plain ESM). The package build
* embeds Include while leaving Loader external, so the built include tree and
* host share one Loader peer. Loader
* settlement rejects startup failures, which `boot` wraps after disposing the
* partial context; a missing fiber or never-activating entry is rejected by
* the final audit, {@link assertEntriesActivated}, which rethrows a plugin's
@@ -729,6 +744,9 @@ export async function assertEntriesActivated(ctx: Context, binName: string): Pro
* @param patches - optional overlay patches applied over the included tree
* (see {@link loadOptionalPatches}); an empty list mounts none.
* @param prepare - optional host setup run after Loader installation and before any config-tree entry mounts.
* @param bareModuleBaseUrl - optional installed-host base for bare package
* names; use it when the host, rather than the configuration project, owns the
* complete plugin set.
* @returns the root context once every entry has started, or as soon as a
* surface disposed the tree while startup was still in flight.
* @throws a labelled error after disposing the partial context — `host
@@ -740,6 +758,7 @@ export async function boot(
absoluteConfigPath: string,
patches?: PatchOptions[],
prepare?: (ctx: Context) => Promise<void> | void,
bareModuleBaseUrl?: string,
): Promise<Context> {
const ctx = new Context()
// Two failure labels: `prepare` runs before any config-tree entry mounts,
@@ -751,7 +770,7 @@ export async function boot(
await ctx.plugin(Loader)
await prepare?.(ctx)
stage = 'plugin tree failed to load'
await mountRootInclude(ctx, absoluteConfigPath, patches)
await mountRootInclude(ctx, absoluteConfigPath, patches, bareModuleBaseUrl)
// A surface can finish and dispose the whole tree while startup is still
// in flight, before the last entry settles. The Loader service goes with
// it, and the activation audit describes a live tree — reading `ctx.loader`

View File

@@ -4,7 +4,7 @@
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-app-boot'

View File

@@ -27,8 +27,8 @@ import {
existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, rmSync, symlinkSync, writeFileSync,
} from 'node:fs'
import { basename, dirname, join } from 'node:path'
import type { EntryOptions } from '@cordisjs/plugin-loader'
import { applyEntryPatches, type PatchOptions } from '@cordisjs/plugin-include'
import type { EntryOptions } from '@deepseek-ai/cordis-plugin-loader'
import { applyEntryPatches, type PatchOptions } from '@deepseek-ai/cordis-plugin-include'
import { resolveDshHome } from '@deepseek-ai/dsh-paths'
import { loadOverlayPatches } from './index.ts'
@@ -51,14 +51,13 @@ export interface DshProfileManifest {
}
/**
* The `dsh`-owned manifest section of a package.json. The nested key names
* the manifest kind: a bundle package declares `bundle`, a profile directory
* declares `profile`; nothing declares both.
* The profile-launcher slice of the `dsh`-owned package.json section. A
* manifest may declare both roles; other consumers own additional keys.
*/
export interface DshManifestSection {
/** Present on bundle packages only. */
/** Bundle metadata consumed by the profile launcher. */
bundle?: DshBundleManifest
/** Present on profile manifests only. */
/** Profile metadata consumed by the profile launcher. */
profile?: DshProfileManifest
}

View File

@@ -1,8 +1,9 @@
import { mkdtempSync, mkdirSync, writeFileSync } from 'node:fs'
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join, resolve, sep } from 'node:path'
import { pathToFileURL } from 'node:url'
import { describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import { Context } from '@deepseek-ai/cordis'
import SystemPrompt, { renderPrompt } from '@deepseek-ai/dsh-system-prompt'
import {
addHarnessSourceSection, assertEntriesActivated, assertEntriesLoaded, boot,
@@ -557,6 +558,73 @@ describe('boot', () => {
}
})
it('can resolve bare plugins from the harness when the config project shadows their package name', async () => {
const dir = tmp()
const harness = tmp()
const absolutePlugin = join(dir, 'absolute.mjs')
const shadow = join(dir, 'node_modules', '@deepseek-ai', 'dsh-system-prompt')
const harnessPlugin = join(harness, 'node_modules', '@deepseek-ai', 'dsh-system-prompt')
mkdirSync(shadow, { recursive: true })
mkdirSync(harnessPlugin, { recursive: true })
writeFileSync(join(shadow, 'package.json'), JSON.stringify({
name: '@deepseek-ai/dsh-system-prompt',
type: 'module',
exports: './index.mjs',
}))
writeFileSync(join(shadow, 'index.mjs'), [
'export function apply(ctx) {',
' ctx.provide("shadowPluginLoaded", true)',
'}',
'',
].join('\n'))
writeFileSync(join(harnessPlugin, 'package.json'), JSON.stringify({
name: '@deepseek-ai/dsh-system-prompt',
type: 'module',
exports: './index.mjs',
}))
writeFileSync(join(harnessPlugin, 'index.mjs'), [
'export function apply(ctx) {',
' ctx.provide("harnessPluginLoaded", true)',
'}',
'',
].join('\n'))
writeFileSync(join(dir, 'relative.mjs'), 'export function apply(ctx) { ctx.provide("relativePluginLoaded", true) }\n')
writeFileSync(absolutePlugin, 'export function apply(ctx) { ctx.provide("absolutePluginLoaded", true) }\n')
const entries = [
'- id: prompt',
" name: '@deepseek-ai/dsh-system-prompt'",
'- id: relative',
" name: './relative.mjs'",
]
const configOwnedPath = join(dir, 'config-owned.cordis.yml')
writeFileSync(configOwnedPath, [...entries, ''].join('\n'))
const hostOwnedPath = join(dir, 'host-owned.cordis.yml')
writeFileSync(hostOwnedPath, [
...entries,
'- id: absolute',
` name: ${JSON.stringify(absolutePlugin)}`,
'',
].join('\n'))
const configOwned = await boot(NAME, configOwnedPath)
try {
expect(configOwned.get('shadowPluginLoaded')).toBe(true)
expect(configOwned.get('systemPrompt')).toBeUndefined()
expect(configOwned.get('relativePluginLoaded')).toBe(true)
} finally {
await configOwned.fiber.dispose()
}
const harnessBaseUrl = pathToFileURL(join(harness, 'entry.mjs')).href
const ctx = await boot(NAME, hostOwnedPath, undefined, undefined, harnessBaseUrl)
try {
expect(ctx.get('harnessPluginLoaded')).toBe(true)
expect(ctx.get('shadowPluginLoaded')).toBeUndefined()
expect(ctx.get('relativePluginLoaded')).toBe(true)
expect(ctx.get('absolutePluginLoaded')).toBe(true)
} finally {
await ctx.fiber.dispose()
}
})
it('runs host preparation before the Loader tree mounts', async () => {
const dir = tmp()
writeFileSync(join(dir, 'noop.mjs'), 'export const name = "noop"\nexport function apply() {}\n')
@@ -631,7 +699,18 @@ describe('boot', () => {
'}',
'',
].join('\n'))
writeFileSync(join(dir, 'cordis.yml'), '- id: exiting\n name: ./exiting.mjs\n')
writeFileSync(join(dir, 'delayed.mjs'), [
'await new Promise(resolve => setTimeout(resolve, 10))',
'export function apply() {}',
'',
].join('\n'))
writeFileSync(join(dir, 'cordis.yml'), [
'- id: exiting',
' name: ./exiting.mjs',
'- id: delayed',
' name: ./delayed.mjs',
'',
].join('\n'))
const ctx = await boot(NAME, join(dir, 'cordis.yml'))
expect(ctx.get('loader')).toBeUndefined()
})
@@ -644,6 +723,25 @@ describe('boot', () => {
)
})
it('labels a deferred config failure with its row and leaves the source file unchanged', async () => {
const dir = tmp()
const configPath = join(dir, 'cordis.yml')
const config = [
'- id: invalid-config',
' name: ./noop.mjs',
' config:',
' value: !!js "JSON.parse(\'invalid\')"',
'',
].join('\n')
writeFileSync(join(dir, 'noop.mjs'), 'export function apply() {}\n')
writeFileSync(configPath, config)
await expect(boot(NAME, configPath)).rejects.toThrow(
'failed to apply loader entry invalid-config (./noop.mjs)',
)
expect(readFileSync(configPath, 'utf8')).toBe(config)
})
it('appends the deepest cause with its original stack to the load failure', async () => {
const dir = tmp()
writeFileSync(join(dir, 'failing.mjs'), [

View File

@@ -12,7 +12,7 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { describe, expect, it, vi } from 'vitest'
import * as yaml from 'js-yaml'
import { entryListSchema } from '@cordisjs/plugin-include'
import { entryListSchema } from '@deepseek-ai/cordis-plugin-include'
import { loadOverlayPatches, renderConfigDump } from '../src/index.ts'
const NAME = 'dsh-test-bin'

View File

@@ -8,8 +8,8 @@ import { mkdtempSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import type { Include } from '@cordisjs/plugin-include'
import { Context } from '@deepseek-ai/cordis'
import type { Include } from '@deepseek-ai/cordis-plugin-include'
import { boot } from '../src/index.ts'
const NAME = 'dsh-test-bin'
@@ -391,7 +391,7 @@ describe('shipped builtins', () => {
it('lets a booted composition share one isolate realm across a group of rows', async () => {
// The reason `boot()` registers `cordis:group`: a composition — notably an
// agent preset living outside this workspace, which cannot resolve
// `@cordisjs/plugin-group` by name — gives a provider and its consumer one
// `@deepseek-ai/cordis-plugin-group` by name — gives a provider and its consumer one
// named realm so the service stays out of the root realm while remaining
// visible to the rows that need it.
const { ctx } = await bootTree([

View File

@@ -3,10 +3,10 @@ import { realpath } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { Context } from 'cordis'
import Hmr from '@cordisjs/plugin-hmr'
import Loader from '@cordisjs/plugin-loader'
import Timer from '@cordisjs/plugin-timer'
import { Context } from '@deepseek-ai/cordis'
import Hmr from '@deepseek-ai/cordis-plugin-hmr'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import Timer from '@deepseek-ai/cordis-plugin-timer'
import { describe, expect, it, vi } from 'vitest'
async function bootHmr(dir: string, root: string[] = [], usePolling?: boolean): Promise<Context> {

View File

@@ -1,218 +0,0 @@
import { execFile } from 'node:child_process'
import { createHash } from 'node:crypto'
import { mkdtemp, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { delimiter, join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { promisify } from 'node:util'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { BUNDLED_PNPM_VERSION, RepositoryCache, type RepositoryInstall } from '@cordisjs/plugin-loader/repository'
const execFileAsync = promisify(execFile)
const roots: string[] = []
/** Normalize Git's platform checkout line endings for source-content assertions. */
const lf = (text: string): string => text.replace(/\r\n/g, '\n')
async function temporaryRoot(name: string): Promise<string> {
const root = await mkdtemp(join(tmpdir(), `cordis-${name}-`))
roots.push(root)
return root
}
async function fakePackage(directory: string): Promise<void> {
const target = join(directory, 'node_modules', 'repository')
await mkdir(target, { recursive: true })
await writeFile(join(target, 'package.json'), '{"name":"fixture"}\n')
}
afterEach(async () => {
vi.unstubAllEnvs()
await Promise.all(roots.splice(0).map(root => rm(root, { recursive: true, force: true })))
})
describe('RepositoryCache', () => {
it('single-flights and permanently reuses an exact specifier', async () => {
const root = await temporaryRoot('repository-cache')
const calls: string[] = []
const install: RepositoryInstall = async (directory) => {
calls.push(directory)
await fakePackage(directory)
}
const cache = new RepositoryCache(root, { install })
const specifier = 'github:owner/repository#0123456789abcdef'
const [first, concurrent] = await Promise.all([cache.resolve(specifier), cache.resolve(specifier)])
expect(concurrent).toBe(first)
expect(calls).toHaveLength(1)
const reopened = new RepositoryCache(root, { install: async () => { throw new Error('cache miss') } })
expect(await reopened.resolve(specifier)).toBe(first)
expect(JSON.parse(await readFile(join(first, '..', '..', 'package.json'), 'utf8'))).toMatchObject({
packageManager: `pnpm@${BUNDLED_PNPM_VERSION}`,
dependencies: { repository: specifier },
})
const second = await cache.resolve('github:owner/repository#fedcba9876543210')
expect(second).not.toBe(first)
expect(calls).toHaveLength(2)
})
it('accepts the valid winner when independent cache instances race', async () => {
const root = await temporaryRoot('repository-race')
const bothStarted = Promise.withResolvers<undefined>()
let starts = 0
const install: RepositoryInstall = async (directory) => {
await fakePackage(directory)
starts += 1
if (starts === 2) bothStarted.resolve(undefined)
await bothStarted.promise
}
const specifier = 'github:owner/repository#race'
const [first, second] = await Promise.all([
new RepositoryCache(root, { install }).resolve(specifier),
new RepositoryCache(root, { install }).resolve(specifier),
])
expect(second).toBe(first)
expect(starts).toBe(2)
expect(await readdir(root)).toHaveLength(1)
})
it('removes a failed staging tree and permits an exact retry', async () => {
const root = await temporaryRoot('repository-retry')
let attempts = 0
const cache = new RepositoryCache(root, { install: async (directory) => {
attempts += 1
if (attempts === 1) throw new Error('install failed')
await fakePackage(directory)
} })
await expect(cache.resolve('github:owner/repository#ref')).rejects.toThrow('failed to prepare repository')
expect(await readdir(root)).toEqual([])
await expect(cache.resolve('github:owner/repository#ref')).resolves.toContain('node_modules')
expect(attempts).toBe(2)
})
it('rejects empty or padded specifiers before touching the cache', async () => {
const root = await temporaryRoot('repository-input')
const cache = new RepositoryCache(root, { install: fakePackage })
expect(() => cache.resolve('')).toThrow('non-empty unpadded string')
expect(() => cache.resolve(' github:owner/repository#ref')).toThrow('non-empty unpadded string')
await expect(readdir(root)).resolves.toEqual([])
})
it('fails loud on a corrupt published marker instead of reinstalling it', async () => {
const root = await temporaryRoot('repository-corrupt')
const specifier = 'github:owner/repository#corrupt'
const key = createHash('sha256').update(specifier).digest('hex')
const entry = join(root, key)
await mkdir(join(entry, 'node_modules', 'repository'), { recursive: true })
await writeFile(join(entry, '.repository-cache.json'), '{}\n')
const cache = new RepositoryCache(root, { install: async () => { throw new Error('must not reinstall') } })
await expect(cache.resolve(specifier)).rejects.toThrow('repository cache marker is invalid')
})
it('isolates and prepares a .dsh-plugin Git subpath from an enclosing pnpm workspace', { timeout: 60_000 }, async () => {
const root = await temporaryRoot('repository-pnpm')
const repository = join(root, 'source')
await mkdir(join(repository, '.dsh-plugin'), { recursive: true })
await mkdir(join(repository, '.dsh-plugin', 'build-helper'), { recursive: true })
await mkdir(join(repository, '.dsh-plugin', 'prepare-helper'), { recursive: true })
await mkdir(join(repository, 'skills', 'fixture'), { recursive: true })
const shadowPnpm = join(root, 'shadow-pnpm')
await mkdir(shadowPnpm)
await writeFile(join(shadowPnpm, 'pnpm'), '#!/bin/sh\nexit 99\n', { mode: 0o700 })
await writeFile(join(shadowPnpm, 'pnpm.bat'), '@exit /b 99\r\n')
await writeFile(join(repository, 'package.json'), `${JSON.stringify({
name: 'repository-fixture',
private: true,
version: '1.0.0',
packageManager: `pnpm@${BUNDLED_PNPM_VERSION}`,
})}\n`)
await writeFile(join(repository, 'pnpm-workspace.yaml'), 'packages: []\n')
await writeFile(join(repository, 'pnpm-lock.yaml'), [
"lockfileVersion: '9.0'",
'settings:',
' autoInstallPeers: true',
' excludeLinksFromLockfile: false',
'importers:',
' .: {}',
'',
].join('\n'))
await writeFile(join(repository, '.dsh-plugin', 'build-helper', 'package.json'), `${JSON.stringify({
name: 'repository-build-helper',
version: '1.0.0',
bin: 'index.js',
})}\n`)
await writeFile(join(repository, '.dsh-plugin', 'build-helper', 'index.js'), [
'#!/usr/bin/env node',
"require('node:fs').writeFileSync('dependency-built.txt', 'dependency available\\n')",
'',
].join('\n'), { mode: 0o700 })
await writeFile(join(repository, '.dsh-plugin', 'prepare-helper', 'package.json'), `${JSON.stringify({
name: 'repository-prepare-helper',
version: '1.0.0',
bin: { 'dsh-plugin-prepare': 'index.js' },
})}\n`)
await writeFile(join(repository, '.dsh-plugin', 'prepare-helper', 'index.js'), [
'#!/usr/bin/env node',
"const { cpSync, mkdirSync, writeFileSync } = require('node:fs')",
"mkdirSync('dsh-plugin-assets/skills', { recursive: true })",
"cpSync('../skills', 'dsh-plugin-assets/skills/0', { recursive: true })",
"writeFileSync('dsh-plugin.mjs', 'export function apply() {}\\n')",
"writeFileSync('prepared.txt', `${process.env.REPOSITORY_TEST_VISIBLE ?? 'absent'}|${process.env.REPOSITORY_TEST_TOKEN ?? 'absent'}|${process.env.PNPM_CONFIG_IGNORE_WORKSPACE ?? 'absent'}\\n`)",
"writeFileSync('environment.json', `${JSON.stringify({ path: process.env.PATH, pathExt: process.env.PATHEXT })}\\n`)",
'',
].join('\n'), { mode: 0o700 })
await writeFile(join(repository, 'skills', 'fixture', 'SKILL.md'), 'repository skill source\n')
await writeFile(join(repository, '.dsh-plugin', 'package.json'), `${JSON.stringify({
name: 'repository-plugin-fixture',
version: '1.0.0',
scripts: {
// The fixture owns dependency installation, not platform-specific
// node_modules/.bin shim generation during pnpm's Git preparation.
prepack: [
'node ./node_modules/repository-build-helper/index.js',
'node ./node_modules/repository-prepare-helper/index.js',
].join(' && '),
},
devDependencies: {
'repository-build-helper': 'file:./build-helper',
'repository-prepare-helper': 'file:./prepare-helper',
},
dsh: { skills: ['../skills'] },
})}\n`)
await execFileAsync('git', ['init', '--quiet'], { cwd: repository })
await execFileAsync('git', ['add', '.'], { cwd: repository })
await execFileAsync('git', [
'-c', 'user.name=Repository Fixture',
'-c', 'user.email=repository@example.invalid',
'commit', '--quiet', '-m', 'fixture',
], { cwd: repository })
const { stdout } = await execFileAsync('git', ['rev-parse', 'HEAD'], { cwd: repository, encoding: 'utf8' })
const specifier = `git+${pathToFileURL(repository).href}#${stdout.trim()}&path:/.dsh-plugin`
vi.stubEnv('REPOSITORY_TEST_VISIBLE', 'visible')
vi.stubEnv('REPOSITORY_TEST_TOKEN', 'hidden')
vi.stubEnv('PNPM_HOME', shadowPnpm)
vi.stubEnv('PATH', [shadowPnpm, ...(process.env.PATH === undefined ? [] : [process.env.PATH])].join(delimiter))
vi.stubEnv('PATHEXT', '.BAT;.CMD;.EXE')
const installed = await new RepositoryCache(join(root, 'cache')).resolve(specifier)
await expect(readFile(join(installed, 'dependency-built.txt'), 'utf8')).resolves.toBe('dependency available\n')
await expect(readFile(join(installed, 'prepared.txt'), 'utf8')).resolves.toBe('visible|absent|true\n')
const environment = JSON.parse(await readFile(join(installed, 'environment.json'), 'utf8')) as {
path: string
pathExt: string
}
expect(environment.path.split(delimiter)).not.toContain(shadowPnpm)
expect(environment.pathExt.split(';')[0]?.toUpperCase()).toBe('.CMD')
await expect(readFile(join(installed, 'dsh-plugin.mjs'), 'utf8')).resolves.toContain('export function apply')
expect(lf(await readFile(join(installed, 'dsh-plugin-assets/skills/0/fixture/SKILL.md'), 'utf8')))
.toBe('repository skill source\n')
await expect(readFile(join(installed, 'package.json'), 'utf8'))
.resolves.toContain('repository-plugin-fixture')
})
})

View File

@@ -9,10 +9,11 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import Hmr from '@cordisjs/plugin-hmr'
import Loader from '@cordisjs/plugin-loader'
import Timer from '@cordisjs/plugin-timer'
import { Context } from '@deepseek-ai/cordis'
import Hmr from '@deepseek-ai/cordis-plugin-hmr'
import Include, { type PatchOptions } from '@deepseek-ai/cordis-plugin-include'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import Timer from '@deepseek-ai/cordis-plugin-timer'
import {
boot,
loadOptionalPatches,
@@ -92,23 +93,101 @@ describe('loadOptionalPatches', () => {
})
})
describe('boot with user patches', () => {
function writeTree(dir: string): string {
writeFileSync(join(dir, 'noop.mjs'), [
'export const name = "noop"',
'export function apply(_ctx, config = {}) {',
' if (config.fail) throw new Error("candidate config failed")',
'}',
function writeTree(dir: string): string {
writeFileSync(join(dir, 'noop.mjs'), [
'export const name = "noop"',
'export function apply(_ctx, config = {}) {',
' if (config.fail) throw new Error("candidate config failed")',
'}',
'',
].join('\n'))
writeFileSync(join(dir, 'cordis.yml'), '- id: noop\n name: ./noop.mjs\n config:\n value: base\n')
return join(dir, 'cordis.yml')
}
function entryConfig(ctx: Context, id: string): unknown {
return [...ctx.loader.entries()].find(entry => entry.options.id === id)?.options.config
}
describe('Loader config interpolation', () => {
it("resolves Include's own !!js options", async () => {
const dir = tmp()
writeFileSync(join(dir, 'noop.mjs'), 'export function apply() {}\n')
writeFileSync(join(dir, 'cordis.yml'), '- id: noop\n name: ./noop.mjs\n')
const ctx = new Context()
await ctx.plugin(Loader)
ctx.loader.builtins.include = Include
ctx.provide('includePath', pathToFileURL(join(dir, 'cordis.yml')).href)
try {
await ctx.loader.create({
name: 'cordis:include',
config: { path: { __jsExpr: "ctx.get('includePath')" } },
})
await ctx.loader.await()
expect([...ctx.loader.entries()].some(entry => entry.options.id === 'noop')).toBe(true)
} finally {
await ctx.fiber.dispose()
}
})
it('waits for row injections before resolving !!js and resolves again after provider replacement', async () => {
const dir = tmp()
writeFileSync(join(dir, 'provider.mjs'), [
'export const name = "provider"',
'export function apply(ctx, config) { ctx.provide("phaseOne", config) }',
'',
].join('\n'))
writeFileSync(join(dir, 'cordis.yml'), '- id: noop\n name: ./noop.mjs\n config:\n value: base\n')
return join(dir, 'cordis.yml')
}
writeFileSync(join(dir, 'reader.mjs'), [
'export const name = "reader"',
'export const inject = ["phaseOne"]',
'export function apply(ctx, config) { ctx.provide("readerResult", config) }',
'',
].join('\n'))
writeFileSync(join(dir, 'cordis.yml'), '[]\n')
const composition: PatchOptions[] = [{
insert: [
{
// Consumer-first order proves interpolation follows injection
// readiness rather than YAML position.
id: 'reader',
name: './reader.mjs',
inject: ['phaseOne'],
config: { value: { __jsExpr: 'ctx.phaseOne.fail ? (() => { throw new Error("rejected provider") })() : ctx.phaseOne.value' } },
},
{ id: 'provider', name: './provider.mjs', config: { value: 'first' } },
],
}]
const ctx = await boot(NAME, join(dir, 'cordis.yml'), composition)
try {
expect(ctx.get('readerResult')).toEqual({ value: 'first' })
const provider = [...ctx.loader.entries()].find(entry => entry.options.id === 'provider')
expect(provider).toBeDefined()
await provider?.update({ disabled: true })
await ctx.loader.await()
expect(ctx.get('readerResult')).toBeUndefined()
await provider?.update({ config: { value: 'second' } })
await provider?.update({ disabled: false })
await ctx.loader.await()
expect(ctx.get('readerResult')).toEqual({ value: 'second' })
function entryConfig(ctx: Context, id: string): unknown {
return [...ctx.loader.entries()].find(entry => entry.options.id === id)?.options.config
}
await provider?.update({ disabled: true })
await provider?.update({ config: { fail: true } })
await provider?.update({ disabled: false })
await expect(ctx.loader.await()).rejects.toThrow('rejected provider')
expect(ctx.get('readerResult')).toBeUndefined()
await provider?.update({ disabled: true })
await provider?.update({ config: { value: 'recovered' } })
await provider?.update({ disabled: false })
await ctx.loader.await()
expect(ctx.get('readerResult')).toEqual({ value: 'recovered' })
} finally {
await ctx.fiber.dispose()
}
})
})
describe('boot with user patches', () => {
it('applies id-targeted overrides, inserts, and interpolates !!js from the environment', async () => {
const dir = tmp()
const userDir = tmp()

View File

@@ -14,6 +14,6 @@ export default defineConfig({
dts: false,
clean: false,
deps: {
alwaysBundle: ['@cordisjs/plugin-include'],
alwaysBundle: ['@deepseek-ai/cordis-plugin-include'],
},
})

View File

@@ -1,6 +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/self-modification/repository-plugin/README.md
README.md: 666f00e02b9ab33bff348df6b4ff90e3f3bfecc7
README.zh.md: b09f68bc17a4eb08df6ecbb3782e14bf26fb7d7f
# pnpm run verify-translation-pairing --write packages/boot/cmdline/README.md
README.md: 98335e901bdf8fe33e14c1ad4c1a320d77f30c96
README.zh.md: 28ea749943c60089c6b4725cb61e121f82aa0114

View File

@@ -0,0 +1,74 @@
# `@deepseek-ai/dsh-cmdline`
English | [中文](README.zh.md)
The command line a dsh launcher hands to the app it boots. The launcher parses only its own flags (`--profile`, `--patch`, the config dumps) and hands **everything after them** to the tree verbatim, so an app owns its flag family, its `--help` text, and its parse errors instead of the launcher knowing them.
## The launcher values
A launcher calls `provideCmdline(ctx, host)` before any tree entry mounts, which provides:
- `ctx.cmdlineArgs` — the invocation's inner arguments. `get()` is the whole interface, and it returns a snapshot: `dsh --profile tui --resume abc` yields `['--resume', 'abc']`.
- `ctx.appExit` — a bounded process-exit request, wired to the launcher's shutdown controller.
An embedding host with no command line provides an empty list; that is the honest answer, not a missing value.
## Ordinary providers and injected config
Any app plugin may inject `cmdlineArgs`, parse it, and publish an ordinary app-owned service. `parseCmdline(ctx, program, plan)` is only a commander adapter; the caller owns the returned value and service:
```ts ignore
export const name = 'web-startup'
export const inject = ['cmdlineArgs']
export function apply(ctx: Context): void {
const values = parseCmdline(ctx, webCommand(), planWebStartup)
if (values !== undefined) ctx.provide('webStartup', values)
}
```
Its Loader row carries no launcher marker or special kind:
```yaml
- id: web-startup
name: '@deepseek-ai/dsh-web-app/startup'
```
Every row configured from those values uses ordinary service injection and direct lazy config access:
```yaml
- id: webserver
name: '@deepseek-ai/dsh-host-webserver'
inject: [webStartup]
config:
host: !!js ctx.webStartup.host ?? '127.0.0.1'
port: !!js ctx.webStartup.port ?? 3080
```
`parseCmdline` parses the immutable arguments and asks `plan` for the app-owned value. On `--help`, `--version`, a parse error, or a `program.error(...)` from the plan, it writes commander's text, requests exit, and returns `undefined`; the provider publishes nothing, so dependent rows never activate.
### How injection orders config
Loader defers a row's `!!js` interpolation until that row's declared injections are active, then evaluates against the row's plugin context. The example above can therefore read `ctx.webStartup` directly: Cordis has already populated that injected service before Loader asks for `webserver`'s config. Include trees preserve nested expression nodes until each target row reaches this point. Provider replacement and live patch reload repeat interpolation against the current injected services, so a launch flag cannot be silently reset.
`enableRow(ctx, id)` turns on a row a bundle ships disabled because only some invocations want it (`dsh web --dev` and its client-plugin reload chain). The activation is an in-memory override: it does not rewrite the row's configured `disabled` value and survives config reapplication for that mounted entry. Loader applies the enabled row's ordinary injection ordering.
### Shared immutable arguments
`get()` does not consume or mutate argv. Multiple plugins can parse the same snapshot and independently provide services. The launcher does not inspect the composition for a command-line owner; a profile with no reader simply ignores its app arguments.
An out-of-tree plugin brings its own commander copy, so commander's control-flow errors are detected structurally rather than by class identity; an identity check would rethrow a printed help as a fatal load failure.
## Model Experience
None, as this package resolves the process's own command line before any session exists.
#### KV Cache effect
None; this package neither assembles nor sends a provider request.
## Known Limitations and Deferred Work
- **Launcher flags must precede app arguments.** The split is positional: the first token the launcher does not recognize starts the inner arguments, so `--patch` placed after an app flag belongs to the app. The launcher's parser consumes one `--`, so an app argument that must survive as a literal `--` needs `-- --`.
- **An app-owned service has no statically declared provider.** Consumer rows name it through ordinary injection; a bundle that omits its provider fails at settlement with pending entries naming the service rather than at load.
- **A user patch that replaces a row's whole `config` drops its expressions.** A flag beats the value written beside it, not a literal a user wrote in place of the expression; keeping the expression is what keeps the flag winning.

View File

@@ -0,0 +1,74 @@
# `@deepseek-ai/dsh-cmdline`
[English](README.md) | 中文
dsh 启动器交给它所引导应用的那条命令行。启动器只解析属于自己的 flag`--profile``--patch`、配置 dump并把**其后的一切**原样交给配置树,因此 flag 家族、`--help` 文本和解析错误都由应用自己持有,启动器不必知道它们。
## 启动器提供的值
启动器在任何配置树条目挂载之前调用 `provideCmdline(ctx, host)`,它提供:
- `ctx.cmdlineArgs`:本次调用的内层参数。`get()` 就是它的全部接口,返回一份快照:`dsh --profile tui --resume abc` 得到 `['--resume', 'abc']`
- `ctx.appExit`:一个有边界的进程退出请求,接到启动器的关停控制器上。
没有命令行的嵌入宿主提供空列表;这是诚实的答案,而不是缺失的值。
## 普通提供方与注入配置
任何应用插件都可以注入 `cmdlineArgs`、解析它,再发布一个普通的应用自有服务。`parseCmdline(ctx, program, plan)` 只适配 commander返回值与服务都归调用方持有
```ts ignore
export const name = 'web-startup'
export const inject = ['cmdlineArgs']
export function apply(ctx: Context): void {
const values = parseCmdline(ctx, webCommand(), planWebStartup)
if (values !== undefined) ctx.provide('webStartup', values)
}
```
它的 Loader 行不携带启动器标记,也没有特殊类型:
```yaml
- id: web-startup
name: '@deepseek-ai/dsh-web-app/startup'
```
所有由这些取值配置的行都使用普通服务注入,并在惰性配置中直接访问该服务:
```yaml
- id: webserver
name: '@deepseek-ai/dsh-host-webserver'
inject: [webStartup]
config:
host: !!js ctx.webStartup.host ?? '127.0.0.1'
port: !!js ctx.webStartup.port ?? 3080
```
`parseCmdline` 解析不可变参数,再向 `plan` 索取应用自有取值。遇到 `--help`、`--version`、解析错误,或 `plan` 发出的 `program.error(...)` 时,它输出 commander 文本、请求退出并返回 `undefined`;提供方什么也不发布,因此依赖行不会激活。
### 注入如何排列配置求值
Loader 会把一行的 `!!js` 插值推迟到该行声明的注入全部激活之后,再基于该行的插件上下文求值。所以上例可以直接读取 `ctx.webStartup`Loader 索取 `webserver` 的配置之前Cordis 已经填入了这个注入服务。Include 树会保留嵌套表达式节点,直到各个目标行到达这一时点。提供方替换与活动 patch 重载都会针对当前注入服务重新插值,因此启动 flag 不会被悄悄重置。
`enableRow(ctx, id)` 打开某个组合包以禁用状态交付、只有部分调用才需要的行(`dsh web --dev` 及其客户端插件重载链路)。该激活是内存中的覆盖:它不会改写行所配置的 `disabled` 值并会在已挂载条目的配置重新应用后继续生效。Loader 会对启用后的行应用普通的注入顺序。
### 共享不可变参数
`get()` 不会消费或修改 argv。多个插件可以解析同一份快照并分别提供服务。启动器不会检查组合中的命令行所有者没有读取方的 profile 只会忽略自己的应用参数。
树外插件会带来自己的一份 commander 副本,因此 commander 的控制流错误按结构识别,而不是按类身份识别;按身份判断会把已经打印出来的 help 重新抛成致命的加载失败。
## 模型体验
无。本包在任何会话存在之前解析进程自身的命令行。
#### KV Cache 影响
无;本包既不组装也不发送提供方请求。
## 已知限制与延期工作
- **启动器的 flag 必须写在应用参数之前**:切分按位置进行,启动器不认识的第一个 token 就是内层参数的起点,因此写在某个应用 flag 之后的 `--patch` 属于应用。启动器的解析器会消耗掉一个 `--`,因此必须以字面量 `--` 存活到应用的参数需要写成 `-- --`。
- **应用自有服务没有静态声明的提供方**:消费行通过普通注入点名它;缺少提供方的组合包会在结算时失败,由待处理条目点名该服务,而不是在加载时失败。
- **用户 patch 若整体替换某行的 `config`,会连同其中的表达式一起丢掉**flag 胜过的是表达式旁写着的那个值,而不是用户用字面量替换掉表达式之后的结果;保留表达式才能保留 flag 的优先级。

View File

@@ -0,0 +1,46 @@
{
"name": "@deepseek-ai/dsh-cmdline",
"description": "Immutable command-line handoff from a dsh launcher to any app plugin that injects cmdlineArgs",
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/boot/cmdline"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
"exports": {
".": {
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./invariant": {
"types": "./lib/types/invariant.d.ts",
"default": "./lib/invariant.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"files": [
"lib/index.js",
"lib/invariant.js",
"lib/types/**/*.d.ts"
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis-plugin-include": "workspace:^",
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"commander": "^15.0.0",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -0,0 +1,174 @@
/**
* @deepseek-ai/dsh-cmdline — the command line a dsh launcher hands to the app
* it boots.
*
* The launcher parses only its own flags (`--profile`, `--patch`, the config
* dumps) and hands everything after them to the tree verbatim through the
* {@link CmdlineArgs} service, so an app owns its flag family, its `--help`
* text, and its parse errors instead of the launcher knowing them.
*
* Any app plugin can inject `cmdlineArgs` and call {@link parseCmdline}. A
* provider may publish the parsed values as its own service, and ordinary rows
* can inject that service and read it from lazily resolved config —
* `port: !!js ctx.webStartup.port ?? 3080` — so a flag beats the value written
* beside it. No row has launcher-level command-line status.
* @module @deepseek-ai/dsh-cmdline
*/
import type { Command } from 'commander'
import type { Context } from '@deepseek-ai/cordis'
// Empty type import carries the Loader Context merge used by enableRow.
import type {} from '@deepseek-ai/cordis-plugin-loader'
/**
* The invocation's inner arguments: everything after the launcher's own flags,
* verbatim and in argv order. `dsh --profile tui --resume abc` yields
* `['--resume', 'abc']`.
*/
export interface CmdlineArgs {
/**
* Read the inner arguments.
* @returns the arguments in argv order; empty when the invocation carried none.
*/
get(): readonly string[]
}
/** Request bounded process exit; the launcher wires it to its shutdown controller. */
export interface AppExit {
/**
* Request exit once the tree has been disposed.
* @param code - the process exit code.
*/
(code: number): void
}
declare module '@deepseek-ai/cordis' {
interface Context {
/** The invocation's inner arguments; provided by a launcher before the tree mounts. */
cmdlineArgs?: CmdlineArgs
/** Bounded process-exit request; provided by a launcher before the tree mounts. */
appExit?: AppExit
}
}
/** The launcher facts an app needs. */
export interface CmdlineHost {
/** The invocation's inner arguments, in argv order. */
args: readonly string[]
/** Bounded process-exit request. */
exit: AppExit
}
/**
* Provide the command line and the exit request on a host context before any
* tree entry mounts. Both are launcher facts, not config: an embedding host
* with no command line provides an empty argument list.
* @param ctx - the host context the tree will mount under.
* @param host - the invocation's arguments and its exit request.
*/
export function provideCmdline(ctx: Context, host: CmdlineHost): void {
const snapshot: readonly string[] = Object.freeze([...host.args])
ctx.provide('cmdlineArgs', { get: () => snapshot })
ctx.provide('appExit', host.exit)
}
/** The process streams commander output is written to; production writes to the process. */
export const internals: { stdout: { write(chunk: string): unknown }; stderr: { write(chunk: string): unknown } } = {
stdout: process.stdout,
stderr: process.stderr,
}
/**
* Resolve parsed arguments into an app-owned value. Call
* `program.error(...)` to reject the invocation with a usage message instead
* of throwing.
* @param program - the parsed commander program.
* @param ctx - the plugin context that received the command line.
* @returns the value an ordinary provider plugin may publish.
*/
export type CmdlinePlan<T = unknown> = (program: Command, ctx: Context) => T
/**
* Parse the launcher's immutable argument snapshot with an app's commander
* program. The caller decides whether and how to publish the returned value;
* this helper has no Loader-row or service ownership semantics.
*
* Help, version, and rejected arguments are terminal for the process: commander
* writes the text, the helper requests `ctx.appExit`, and it returns
* `undefined` so the caller publishes nothing.
* @param ctx - plugin context carrying `cmdlineArgs` and `appExit`.
* @param program - the app's commander program, with its flags and description already declared.
* @param plan - this invocation's resolved value; omitted returns an empty object.
* @returns the resolved value, or `undefined` when the app asked to exit.
* @throws when the launcher did not provide the command line and exit request.
*/
export function parseCmdline<T>(
ctx: Context,
program: Command,
plan: CmdlinePlan<T> = (() => ({}) as T),
): T | undefined {
// Read through the global service store, not the property proxy: appExit is
// an optional host value and the plugin only needs to inject cmdlineArgs.
const args = ctx.get('cmdlineArgs')
const exit = ctx.get('appExit')
if (args === undefined || exit === undefined) {
throw new Error(`${program.name()}: the launcher must provide ctx.cmdlineArgs and ctx.appExit before the tree mounts`)
}
program
.exitOverride()
.configureOutput({
writeOut: text => void internals.stdout.write(text),
writeErr: text => void internals.stderr.write(text),
})
try {
program.parse(args.get(), { from: 'user' })
return plan(program, ctx)
} catch (error) {
// exitOverride turns help, version, a parse error, and a plan's own
// program.error() into a CommanderError; commander has already written the
// text through the output configured above.
if (!isCommanderError(error)) throw error
exit(error.exitCode)
return undefined
}
}
/**
* Turn on a row this composition ships disabled, because this invocation asked
* for it (`dsh web --dev` and its client-plugin reload chain).
*
* A row cannot be inserted from inside a mounting plugin — the Loader returns a
* prefixed id it then fails to resolve — so a conditional row ships disabled
* and a row mounted beside it enables it after startup resolves the invocation.
* The Loader keeps that activation in memory, separate from serialized options,
* so reapplying the composition cannot restore the invocation's row to disabled.
* @param ctx - plugin context whose Loader tree carries the row.
* @param id - the row id.
* @returns nothing once the row has started or is waiting for its dependencies.
* @throws when the Loader or named row is absent.
*/
export async function enableRow(ctx: Context, id: string): Promise<void> {
const loader = ctx.get('loader')
if (loader === undefined) throw new Error('dsh-cmdline: enabling a row requires the Loader service')
const entry = [...loader.entries()].find(candidate => candidate.options.id === id)
if (entry === undefined) throw new Error(`dsh-cmdline: the composition has no ${JSON.stringify(id)} row to enable`)
await entry.enableRuntime()
}
/**
* Whether a thrown value is commander's own control-flow error (help, version,
* a parse error, or `program.error`).
*
* Detected structurally, not with `instanceof`: an out-of-tree plugin brings
* its own commander copy, whose `CommanderError` class is a different identity
* from this package's, and an identity check there would rethrow a printed
* help as a fatal load failure.
* @param error - the thrown value.
* @returns true when the value carries commander's error code and exit code.
*/
function isCommanderError(error: unknown): error is { code: string; exitCode: number } {
if (typeof error !== 'object' || error === null) return false
const candidate = error as { code?: unknown; exitCode?: unknown }
return typeof candidate.code === 'string' && candidate.code.startsWith('commander.')
&& typeof candidate.exitCode === 'number'
}

View File

@@ -0,0 +1,30 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-cmdline`.
* @module @deepseek-ai/dsh-cmdline/invariant
*/
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-cmdline'
/** Cordis companion plugin name. */
export const name = 'cmdline-invariant'
/** Service required before the companion can register. */
export const inject = ['invariants']
/**
* No runtime invariant: `cmdlineArgs` is an immutable launcher fact that any
* number of ordinary plugins may read. App-owned providers and consumers use
* normal Cordis service injection, whose missing dependencies are already
* reported by Loader settlement.
*/
const install: InvariantInstaller = () => {}
/**
* Register this package's invariant companion.
* @param ctx - Cordis context carrying the invariant service.
* @returns the installed registration's disposer after setup succeeds.
*/
export const apply = (ctx: Context): Promise<() => void> =>
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))

View File

@@ -0,0 +1,265 @@
/**
* The launcher-to-app command line over a REAL Loader tree, mounted the way a
* profile boot mounts it: Loader holds each row until its injections are
* active, then resolves that row's config against its injection-ready context.
*/
import { mkdtempSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { Command } from 'commander'
import { Context } from '@deepseek-ai/cordis'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import Include from '@deepseek-ai/cordis-plugin-include'
import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include'
import { afterEach, describe, expect, it } from 'vitest'
import {
enableRow, internals, parseCmdline, provideCmdline, type CmdlinePlan,
} from '../src/index.ts'
/** Every value one boot of the fixture tree observed. */
interface Observed {
/** Config the reading row started with; absent means it never started. */
started?: Record<string, unknown>
exits: number[]
out: string
}
/** A booted fixture tree: what it observed, and its root for direct parser calls. */
interface Fixture {
observed: Observed
ctx: Context
}
const disposers: (() => Promise<void>)[] = []
afterEach(async () => {
for (const dispose of disposers.splice(0)) await dispose()
internals.stdout = process.stdout
internals.stderr = process.stderr
})
/** The fixture app's flag family: one `--port` its rows read from the service. */
function demoCommand(): Command {
return new Command().name('demo').exitOverride().option('--port <port>', 'listen port')
}
/** The fixture app's plan: the resolved values its rows read. */
const demoPlan: CmdlinePlan<{ port?: number }> = (program) => {
const port = program.opts<{ port?: string }>().port
if (port === undefined) return {}
if (!/^\d+$/.test(port)) program.error(`error: --port must be a number, got ${JSON.stringify(port)}`)
return { port: Number(port) }
}
/** A YAML `!!js` expression node, as the include parses one out of a patch file. */
const expression = (source: string): unknown => ({ __jsExpr: source })
/**
* Mount a two-row composition the way a profile boot does: both rows at once,
* with Loader ordering config resolution from their injections.
* @param args - the invocation's inner arguments.
* @param plan - the app's plan; defaults to the fixture's own.
* @returns the booted fixture.
*/
async function bootFixture(
args: string[],
plan: CmdlinePlan = demoPlan,
options: { objectInject?: boolean; withoutProvider?: boolean } = {},
): Promise<Fixture> {
const dir = mkdtempSync(join(tmpdir(), 'dsh-cmdline-'))
const observed: Observed = { exits: [], out: '' }
writeFileSync(join(dir, 'reader.mjs'), `
export const name = 'reader'
export const inject = ['demoStartup']
export function apply(ctx, config) { globalThis.__observed.started = config }
`)
// The Loader imports a row through Node's own resolver, which cannot resolve
// this workspace's sources; the row delegates to the real function the test
// imported through the source-plane path mapping.
writeFileSync(join(dir, 'startup.mjs'), `
export const name = 'demo-startup'
export const inject = ['cmdlineArgs']
export function apply(ctx) { return globalThis.__provideDemoArgs(ctx) }
`)
writeFileSync(join(dir, 'cordis.yml'), '[]\n')
const observing = { write: (chunk: string) => { observed.out += chunk; return true } }
internals.stdout = observing
internals.stderr = observing
const globals = globalThis as unknown as { __observed: Observed; __provideDemoArgs: (ctx: Context) => void }
globals.__observed = observed
globals.__provideDemoArgs = (ctx: Context) => {
const values = parseCmdline(ctx, demoCommand(), plan)
if (values !== undefined) ctx.provide('demoStartup', values)
}
// The composition, exactly as a profile delivers one: include patches whose
// config carries `!!js` expressions.
const composition: PatchOptions[] = [{
insert: [
...options.withoutProvider === true
? []
: [{ id: 'demo-startup', name: pathToFileURL(join(dir, 'startup.mjs')).href }],
{
id: 'reader',
name: pathToFileURL(join(dir, 'reader.mjs')).href,
inject: options.objectInject === true ? { demoStartup: { required: true } } : ['demoStartup'],
config: { port: expression('ctx.demoStartup.port ?? 3080') },
},
],
}]
const ctx = new Context()
await ctx.plugin(Loader)
ctx.loader.builtins.include = Include
provideCmdline(ctx, { args, exit: code => void observed.exits.push(code) })
await ctx.loader.create({
name: 'cordis:include',
config: { path: pathToFileURL(join(dir, 'cordis.yml')).href, patches: structuredClone(composition) },
})
await ctx.loader.await()
disposers.push(async () => { await ctx.fiber.dispose() })
return { observed, ctx }
}
describe('parseCmdline', () => {
it('lets a row read the flag value the app resolved', async () => {
const { observed } = await bootFixture(['--port', '8080'])
expect(observed.started).toEqual({ port: 8080 })
expect(observed.exits).toEqual([])
})
it('leaves a row on the value written beside the expression when no flag names one', async () => {
const { observed } = await bootFixture([])
expect(observed.started).toEqual({ port: 3080 })
})
it('recognizes the Loader object form of a provider-service injection', async () => {
const { observed } = await bootFixture(['--port', '8080'], demoPlan, { objectInject: true })
expect(observed.started).toEqual({ port: 8080 })
})
it('prints the app help, starts no reading row, and requests exit 0', async () => {
const { observed } = await bootFixture(['--help'])
expect(observed.out).toContain('Usage: demo')
expect(observed.started).toBeUndefined()
expect(observed.exits).toEqual([0])
})
it('rejects the invocation from the plan without starting the app', async () => {
const { observed } = await bootFixture(['--port', 'abc'])
expect(observed.out).toContain('--port must be a number')
expect(observed.started).toBeUndefined()
expect(observed.exits).toEqual([1])
})
it('rethrows a plan failure that is not commander asking to exit', async () => {
const { ctx } = await bootFixture([], demoPlan, { withoutProvider: true })
const plan: CmdlinePlan = () => { throw new Error('plan exploded') }
expect(() => { parseCmdline(ctx, demoCommand(), plan) }).toThrow('plan exploded')
})
it('rethrows a thrown value that is not an object at all', async () => {
const { ctx } = await bootFixture([], demoPlan, { withoutProvider: true })
const plan: CmdlinePlan = () => {
const thrown: unknown = 'plan threw a string'
throw thrown
}
expect(() => { parseCmdline(ctx, demoCommand(), plan) }).toThrow('plan threw a string')
})
it('returns values without inspecting Loader rows or owning a service', async () => {
const { ctx } = await bootFixture([], demoPlan, { withoutProvider: true })
expect(parseCmdline(ctx, demoCommand())).toEqual({})
expect(ctx.get('demoStartup')).toBeUndefined()
})
})
describe('enableRow', () => {
it('enables the named Loader row and fails loud when the Loader or row is absent', async () => {
const withoutLoader = new Context()
await expect(enableRow(withoutLoader, 'client-hmr')).rejects.toThrow('requires the Loader service')
const ctx = new Context()
let enabled = false
ctx.provide('loader', {
entries: () => [{
options: { id: 'client-hmr' },
enableRuntime: async () => { enabled = true },
}],
} as never)
await enableRow(ctx, 'client-hmr')
expect(enabled).toBe(true)
await expect(enableRow(ctx, 'absent')).rejects.toThrow('no "absent" row to enable')
})
it('keeps invocation-only activation through config reapplication', async () => {
const dir = mkdtempSync(join(tmpdir(), 'dsh-runtime-enable-'))
const observed = { starts: 0, stops: 0 }
;(globalThis as unknown as { __runtimeEnableObserved: typeof observed }).__runtimeEnableObserved = observed
writeFileSync(join(dir, 'conditional.mjs'), `
export function apply(ctx) {
globalThis.__runtimeEnableObserved.starts += 1
ctx.effect(() => () => { globalThis.__runtimeEnableObserved.stops += 1 })
}
`)
writeFileSync(join(dir, 'cordis.yml'), [
'- id: conditional',
` name: ${pathToFileURL(join(dir, 'conditional.mjs')).href}`,
' disabled: true',
'',
].join('\n'))
const ctx = new Context()
await ctx.plugin(Loader)
ctx.loader.builtins.include = Include
await ctx.loader.create({
name: 'cordis:include',
config: { path: pathToFileURL(join(dir, 'cordis.yml')).href },
})
await ctx.loader.await()
const conditional = [...ctx.loader.entries()].find(entry => entry.options.id === 'conditional')
const include = [...ctx.loader.entries()].find(entry => entry.options.name === 'cordis:include')
expect(conditional).toBeDefined()
expect(include?.fiber).toBeDefined()
expect(conditional?.options.disabled).toBe(true)
expect(observed).toEqual({ starts: 0, stops: 0 })
await enableRow(ctx, 'conditional')
await ctx.loader.await()
expect(conditional?.disabled).toBe(false)
expect(conditional?.options.disabled).toBe(true)
expect(observed).toEqual({ starts: 1, stops: 0 })
await include!.fiber!.update(include!.options.config, true)
await ctx.loader.await()
expect(conditional?.disabled).toBe(false)
expect(conditional?.options.disabled).toBe(true)
expect(observed).toEqual({ starts: 1, stops: 0 })
disposers.push(async () => { await ctx.fiber.dispose() })
})
})
describe('provideCmdline', () => {
it('hands the app a snapshot the caller cannot mutate afterwards', () => {
const ctx = new Context()
const args = ['--resume', 'abc']
provideCmdline(ctx, { args, exit: () => {} })
args.push('--tampered')
expect(ctx.cmdlineArgs?.get()).toEqual(['--resume', 'abc'])
})
it('fails loud when a parser runs without the launcher values', () => {
const ctx = new Context()
expect(() => { parseCmdline(ctx, demoCommand()) })
.toThrow('the launcher must provide ctx.cmdlineArgs and ctx.appExit')
})
it('lets multiple parsers read the same immutable snapshot', () => {
const ctx = new Context()
provideCmdline(ctx, { args: ['--port', '8080'], exit: () => {} })
expect(parseCmdline(ctx, demoCommand(), demoPlan)).toEqual({ port: 8080 })
expect(parseCmdline(ctx, demoCommand(), demoPlan)).toEqual({ port: 8080 })
expect(Object.isFrozen(ctx.cmdlineArgs?.get())).toBe(true)
})
})

View File

@@ -8,24 +8,15 @@
"src"
],
"references": [
{
"path": "../../../vendor/cosmokit"
},
{
"path": "../../../vendor/cordis"
},
{
"path": "../../../vendor/include"
},
{
"path": "../../../vendor/loader"
},
{
"path": "../../skill/skill-local"
},
{
"path": "../../mcp/mcp-client"
},
{
"path": "../../util/paths"
},
{
"path": "../../support/invariants"
}

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 packages/bundle/base/README.md
README.md: 2a87b01ad4819750a58163f8c472e61ea633588e
README.zh.md: dc79895355546812aa3371487190724f169c6260
README.md: 8b0db20274036a2601da19617a35e6bf4aeb30ca
README.zh.md: ac5ab10a523fa211c1c1daf4c55d4dc8702eb782

View File

@@ -2,7 +2,7 @@
English | [中文](README.zh.md)
The shared dsh core as a profile bundle: [`cordis.patch.yml`](cordis.patch.yml) inserts every base plugin row — model adapters, the shared [`agent-default-model`](../../core/agent-default-model/README.md) selection, tools, persistence, policy, settings/credentials, repository Plugins, telemetry — over the empty profile root, as the first layer of every profile's `dsh.profile.bundles` list. Later bundle layers (e.g. [`dsh-web-app`](../web-app/README.md)) and the user's profile `cordis.patch.yml` override these rows by id; a patch replaces a row's whole `config`, so mode-specific values live in mode bundles, not here. The package has no runtime API; the profile composer resolves the patch through the `dsh.bundle.patch` manifest field, never through code.
The shared dsh core as a profile bundle: [`cordis.patch.yml`](cordis.patch.yml) inserts every base plugin row — model adapters, the shared [`agent-default-model`](../../core/agent-default-model/README.md) selection, tools, persistence, policy, settings/credentials, telemetry, and host-level subagent providers — over the empty profile root, as the first layer of every profile's `dsh.profile.bundles` list. Codex and Claude Code providers load dormant; Agent Presets independently decide whether their agent contributes either model-facing delegation tool. Later bundle layers (e.g. [`dsh-web-app`](../web-app/README.md)) and the user's profile `cordis.patch.yml` override these rows by id; a patch replaces a row's whole `config`, so mode-specific values live in mode bundles, not here. The package has no runtime API; the profile composer resolves the patch through the `dsh.bundle.patch` manifest field, never through code.
Windows hosts booting a shipped profile additionally receive [`windows.cordis.patch.yml`](windows.cordis.patch.yml): it disables the POSIX-only bash stack (`bash-sandbox`/`tool-bash`) and inserts the sandbox-confined PowerShell stack (`@deepseek-ai/dsh-pwsh-sandbox`, `@deepseek-ai/dsh-tool-pwsh`). The permission surface stays exactly as on POSIX: `sandbox`/`sandbox-policy` enforce the file-effect policy through the Windows ACL restricted-token runner (the win32 chain of `dsh-sandbox-local``@deepseek-ai/dsh-sandbox-windows-acl`), the permission switcher and the approval service run unchanged, and `fs-sandbox` keeps fencing `ctx.fs` writes — mounting `dsh-fs-local` alongside it would double-register `ctx.fs` and fail the load. The launcher applies the layer between the bundle layers and the user layers on win32 hosts; a Windows host that prefers the unconfined local pwsh executor or full access overrides these rows through its profile or home `cordis.patch.yml` (the bash-restore recipe must be complete: disable `pwsh-sandbox`/`tool-pwsh` AND re-enable `bash-sandbox`/`tool-bash` — both executor families register the same `bash` service, so an incomplete recipe fails loud at load). POSIX hosts never receive it.
@@ -19,4 +19,5 @@ None directly; each inserted row's package owns its effect.
## Known Limitations and Deferred Work
- **A patch replaces whole row configs** — profile overrides must restate every field a row keeps; there is no deep-merge layer.
- **Claude's SDK platform CLI remains in the Profile install closure** — the base bundle depends on the Claude provider, whose production path resolves the host `claude`; removing the SDK's unused optional payload is deferred to the product installation-closure follow-up.
- **The Windows temp grant is a private per-session subdirectory** — `workspace-write` confines writes to the workspace plus the session's own temp subdirectory (`<temp>\dsh-<hash>`, TMP/TEMP rewritten for confined children); `read-only` grants nothing. See `@deepseek-ai/dsh-sandbox-windows-acl`.

View File

@@ -2,7 +2,7 @@
[English](README.md) | 中文
以 profile 组合包形式交付的共享 dsh 核心:[`cordis.patch.yml`](cordis.patch.yml) 在空的 profile 根之上插入全部基础插件行——模型适配器、共享的 [`agent-default-model`](../../core/agent-default-model/README.md) 选择、工具、持久化、策略、settingscredentials、repository 插件、遥测——作为每个 profile 的 `dsh.profile.bundles` 列表中的第一层。后续的组合包层(例如 [`dsh-web-app`](../web-app/README.md))和用户 profile 的 `cordis.patch.yml` 按 id 覆盖这些行patch 会替换目标行的整个 `config`,因此模式专属的值放在各模式组合包中,而不是这里。该包没有运行时 APIprofile 组合器通过 manifest元数据清单`dsh.bundle.patch` 字段解析 patch绝不通过代码。
以 profile 组合包形式交付的共享 dsh 核心:[`cordis.patch.yml`](cordis.patch.yml) 在空的 profile 根之上插入全部基础插件行——模型适配器、共享的 [`agent-default-model`](../../core/agent-default-model/README.md) 选择、工具、持久化、策略、settingscredentials、遥测与宿主级 subagent provider——作为每个 profile 的 `dsh.profile.bundles` 列表中的第一层。Codex 与 Claude Code provider 以休眠状态加载Agent Preset 分别决定自己的 agent 是否贡献任一面向模型的委派工具。后续的组合包层(例如 [`dsh-web-app`](../web-app/README.md))和用户 profile 的 `cordis.patch.yml` 按 id 覆盖这些行patch 会替换目标行的整个 `config`,因此模式专属的值放在各模式组合包中,而不是这里。该包没有运行时 APIprofile 组合器通过 manifest元数据清单`dsh.bundle.patch` 字段解析 patch绝不通过代码。
启动交付 profile 的 Windows 主机还会额外收到 [`windows.cordis.patch.yml`](windows.cordis.patch.yml):它禁用仅 POSIX 的 bash 栈(`bash-sandbox`/`tool-bash`),并插入沙盒受限的 PowerShell 栈(`@deepseek-ai/dsh-pwsh-sandbox``@deepseek-ai/dsh-tool-pwsh`)。权限面与 POSIX 完全一致:`sandbox`/`sandbox-policy` 通过 Windows ACL 受限令牌 runner`dsh-sandbox-local` 的 win32 链 → `@deepseek-ai/dsh-sandbox-windows-acl`)执行文件效果策略,权限切换器与 approval 服务原样运行,`fs-sandbox` 继续围栏 `ctx.fs` 写入——在其旁再挂载 `dsh-fs-local` 会重复注册 `ctx.fs` 并在加载时失败。启动器在 win32 主机上把该层应用于 bundle 层与用户层之间;偏好不限权本地 pwsh 执行器或完整访问的 Windows 主机通过其 profile 或 home 的 `cordis.patch.yml` 覆盖这些行bash 恢复配方必须完整:禁用 `pwsh-sandbox`/`tool-pwsh` 并重新启用 `bash-sandbox`/`tool-bash`——两个执行器家族注册同一个 `bash` 服务,配方不完整会在加载时 fail loud。POSIX 主机永远不会收到它。
@@ -19,4 +19,5 @@
## 已知限制与延期工作
- **patch 会替换整行 `config`**profile 覆盖必须重述该行需要保留的每个字段;不存在深度合并层。
- **Claude SDK 的平台 CLI命令行界面仍在 Profile 安装闭包中**base 组合包依赖 Claude 提供方,其生产路径解析宿主提供的 `claude`;移除 SDK 中未使用的可选载荷,推迟到产品安装闭包后续项处理。
- **Windows 的临时目录授权是按会话的私有子目录**——`workspace-write` 把写入限制在工作区与会话自己的 temp 子目录(`<temp>\dsh-<hash>`,受限子进程的 TMP/TEMP 被改写);`read-only` 不授予任何写入。见 `@deepseek-ai/dsh-sandbox-windows-acl`

View File

@@ -14,20 +14,13 @@
- insert:
- id: timer
name: '@cordisjs/plugin-timer'
name: '@deepseek-ai/cordis-plugin-timer'
- id: hmr
name: '@cordisjs/plugin-hmr'
name: '@deepseek-ai/cordis-plugin-hmr'
config:
root: ['.']
# The profile's cordis.patch.yml replaces this row's config to select exact GitHub
# repository Plugin generations. The app registers the DSH-owned runtime even
# when the list is empty so a later personal-config edit can load
# transactionally; one-shot headless runs consume the startup value only.
- id: repository-plugins
name: '@deepseek-ai/dsh-repository-plugin'
- id: llm
name: '@deepseek-ai/dsh-llm'
@@ -294,6 +287,15 @@
config:
providerName: fork
# Product providers stay on the host plane because the registry is a
# process singleton. Agent presets decide whether their own model sees the
# matching delegation tools; loading either provider starts no product.
- id: subagent-codex
name: '@deepseek-ai/dsh-subagent-codex'
- id: subagent-claude-code
name: '@deepseek-ai/dsh-subagent-claude-code'
# Continuable background children are selected per delegation tool. The
# separately loaded follow-up tool registers the one global `send_message`.
- id: tool-subagent-control

View File

@@ -1,8 +1,15 @@
{
"name": "@deepseek-ai/dsh-base",
"description": "The shared dsh core as a profile bundle: every profile's first patch layer, inserting the base plugin rows over the empty profile root",
"version": "0.0.1",
"private": true,
"version": "0.0.1-rc.1",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/bundle/base"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
@@ -34,8 +41,8 @@
}
},
"dependencies": {
"@cordisjs/plugin-hmr": "workspace:*",
"@cordisjs/plugin-timer": "workspace:*",
"@deepseek-ai/cordis-plugin-hmr": "workspace:^",
"@deepseek-ai/cordis-plugin-timer": "workspace:^",
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-agent-default-model": "workspace:^",
"@deepseek-ai/dsh-agent-loop": "workspace:^",
@@ -63,7 +70,6 @@
"@deepseek-ai/dsh-plan-mode": "workspace:^",
"@deepseek-ai/dsh-pwsh-sandbox": "workspace:^",
"@deepseek-ai/dsh-repeat-tool-guard": "workspace:^",
"@deepseek-ai/dsh-repository-plugin": "workspace:^",
"@deepseek-ai/dsh-sandbox-local": "workspace:^",
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
@@ -81,6 +87,8 @@
"@deepseek-ai/dsh-spill-local": "workspace:^",
"@deepseek-ai/dsh-spill-policy": "workspace:^",
"@deepseek-ai/dsh-subagent": "workspace:^",
"@deepseek-ai/dsh-subagent-claude-code": "workspace:^",
"@deepseek-ai/dsh-subagent-codex": "workspace:^",
"@deepseek-ai/dsh-subagent-fork": "workspace:^",
"@deepseek-ai/dsh-subagent-spawn": "workspace:^",
"@deepseek-ai/dsh-subprocess-local": "workspace:^",
@@ -114,11 +122,11 @@
"@deepseek-ai/dsh-workspace-context": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "^0.0.1",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"cordis": "^4.0.0-rc.7"
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -3,7 +3,7 @@
* @module @deepseek-ai/dsh-base/invariant
*/
import type { Context } from 'cordis'
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-base'

Some files were not shown because too many files have changed in this diff Show More