mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-14 04:01:35 +00:00
Merge remote-tracking branch 'origin/xtr/session-format-migration' into xtr/message-tool-call-id
# Conflicts: # .agents/notes/implemented/architecture/2026-06-20-branded-ids.i18n.yaml # .agents/notes/implemented/architecture/2026-06-20-branded-ids.md # .agents/notes/implemented/architecture/2026-06-20-branded-ids.zh.md # packages/fs/tool-fs/tests/read-image.spec.ts # packages/llm/llm-deepseek/tests/adapter.e2e.ts # packages/llm/llm-deepseek/tests/serialize.spec.ts # packages/llm/llm-pi-ai/src/context.ts # packages/llm/llm-pi-ai/tests/context.spec.ts # packages/llm/llm-pi-ai/tests/convert.spec.ts # packages/llm/llm/src/message.ts # packages/llm/llm/tests/content.spec.ts
This commit is contained in:
@@ -58,9 +58,10 @@ const releaseMemberDirectory = /^(?:packages\/(?!experimental\/)[^/]+\/[^/]+|app
|
||||
const localArtifactDirs = new Set(['node_modules'])
|
||||
const appPackageFiles: Readonly<Record<string, readonly string[]>> = {
|
||||
'@deepseek-ai/dsh': ['lib/*.js', 'config'],
|
||||
// The Web build emits sourcemaps for browser debugging; publishing them is
|
||||
// what the payload policy forbids, so the bundle ships without them.
|
||||
'@deepseek-ai/dsh-web-frontend': ['dist', '!dist/**/*.map'],
|
||||
// Sourcemaps stay out by payload policy; the worker-preview surface
|
||||
// (dist/preview.html and dist/preview/) backs private experimental
|
||||
// packages and is not published.
|
||||
'@deepseek-ai/dsh-web-frontend': ['dist', '!dist/**/*.map', '!dist/preview.html', '!dist/preview'],
|
||||
}
|
||||
|
||||
/** The subset of package.json fields this constraint check cares about. */
|
||||
@@ -161,6 +162,10 @@ const packageFileExtras: Readonly<Record<string, readonly string[]>> = {
|
||||
// SQLite loads every statement from immutable package resources at runtime.
|
||||
'@deepseek-ai/dsh-session-persistence-sqlite': ['resources/sql/**/*.sql'],
|
||||
'@deepseek-ai/dsh-skill-badge': ['assets'],
|
||||
// tsdown shares the repository/pack code between the lib entry and the bin
|
||||
// through a hashed chunk. The committed bin.js is the link target pnpm can
|
||||
// resolve at install time, before the build produces lib/bin.js.
|
||||
'@deepseek-ai/dsh-experimental-webworker-packer': ['bin.js', 'lib/repository-*.js'],
|
||||
'@deepseek-ai/dsh-subprocess-local': ['scripts/ensure-spawn-helper.mjs'],
|
||||
}
|
||||
|
||||
@@ -181,7 +186,11 @@ function expectedDshPackageFiles(manifest: PackageManifest): readonly string[] {
|
||||
// bundle; the package-invariant gate validates the companion itself.
|
||||
'lib/invariant.js',
|
||||
...manifest.bin ? ['lib/bin.js'] : [],
|
||||
...manifest.exports?.['./worker'] ? ['lib/worker.cjs'] : [],
|
||||
// Worker-thread packages ship a CJS worker entry; the browser worker
|
||||
// bundle is an ES module a page loads with `new Worker(type: 'module')`.
|
||||
// Keyed on the artifact path, like ./client below.
|
||||
...exportDefault(manifest, './worker') === './lib/worker.cjs' ? ['lib/worker.cjs'] : [],
|
||||
...exportDefault(manifest, './worker') === './lib/worker.js' ? ['lib/worker.js'] : [],
|
||||
// UI plugin packages ship their browser bundle beside the node lib
|
||||
// (single-artifact ruling: dist/ retired, ./client resolves lib/client.js).
|
||||
// Keyed on the artifact path, not the subpath name: apiproxy's ./client is
|
||||
|
||||
@@ -42,7 +42,7 @@ const MAX_DECL_CHARS = 1200
|
||||
/**
|
||||
* Line budget for ONE slot's expanded report. The whole point of narrowing to a
|
||||
* single slot is to spend less context, so a report a model cannot finish
|
||||
* reading is a defect rather than a detail. Today's widest slot renders 60
|
||||
* reading is a defect rather than a detail. The widest measured slot renders 60
|
||||
* lines, so this leaves room to document a slot properly while catching the two
|
||||
* ways a report runs away: an owner share that hands down a subsystem instead of
|
||||
* a share, and prose that grew into a manual.
|
||||
@@ -552,7 +552,6 @@ export function main(): void {
|
||||
console.log(`gen-client-catalog: wrote ${OUT}.`)
|
||||
}
|
||||
|
||||
// Run only when invoked as a script, not when imported by a test.
|
||||
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
|
||||
main()
|
||||
}
|
||||
|
||||
@@ -874,7 +874,6 @@ function main(): void {
|
||||
console.log(`gen-config-catalog: wrote ${OUT}.`)
|
||||
}
|
||||
|
||||
// Run only when invoked as a script, not when imported by a test.
|
||||
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
|
||||
main()
|
||||
}
|
||||
|
||||
@@ -293,6 +293,8 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
|
||||
ApprovalService: 'approval.md',
|
||||
EncodedImageAttachment: 'attachment.md',
|
||||
ImageAttachmentRef: 'attachment.md',
|
||||
ImageRequestPolicy: 'attachment.md',
|
||||
RequestImageAttachment: 'attachment.md',
|
||||
SaveImageAttachment: 'attachment.md',
|
||||
StoredImageAttachment: 'attachment.md',
|
||||
ShellExecRequest: 'shell.md',
|
||||
@@ -1006,7 +1008,6 @@ export function main(): void {
|
||||
console.log(`gen-cordis-catalog: ${outputs.length} artifact(s) computed, ${changedPages} written, ${recorded} pair record(s) refreshed.`)
|
||||
}
|
||||
|
||||
// Run only when invoked as a script, not when imported by a test.
|
||||
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
|
||||
main()
|
||||
}
|
||||
|
||||
@@ -466,7 +466,6 @@ function main(): void {
|
||||
}
|
||||
}
|
||||
|
||||
// Run only when invoked as a script, not when imported by a test.
|
||||
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
|
||||
main()
|
||||
}
|
||||
|
||||
@@ -768,7 +768,6 @@ function main(): void {
|
||||
console.log(`gen-third-party-notices: wrote ${OUT}.`)
|
||||
}
|
||||
|
||||
// Run only when invoked as a script, not when imported by a test.
|
||||
if (process.argv[1] !== undefined && import.meta.filename === resolve(process.argv[1])) {
|
||||
main()
|
||||
}
|
||||
|
||||
@@ -314,18 +314,18 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
pkg: '@deepseek-ai/dsh-tool-fs',
|
||||
dir: 'tool-fs',
|
||||
source: 'packages/fs/tool-fs/src/index.ts',
|
||||
requires: ['ctx.tools', 'ctx.fs', 'ctx.systemPrompt', 'ctx.attachments (read_image registration)', 'ctx.llm + an image-capable route (read_image execution)'],
|
||||
requires: ['ctx.tools', 'ctx.fs', 'ctx.systemPrompt', 'ctx.attachments (image-tool registration)', 'ctx.llm + an image-capable route (image-tool execution)'],
|
||||
writes: ['tool/call', 'fs/write-intent or fs/edit-intent for mutations', 'fs/observed after read presence/absence or successful file operation', 'durable attachment (read_image)', 'tool/result'],
|
||||
async mount(ctx) {
|
||||
// The tool needs `fs`; the bare provider is sufficient because policy
|
||||
// changes behavior, not schema shape. The catalog seam marker opts into
|
||||
// the attachments-conditional read_image schema without attachment I/O.
|
||||
// the attachments-conditional image schema without attachment I/O.
|
||||
await ctx.plugin(LocalFileSystem)
|
||||
await ctx.plugin(CatalogAttachmentStore)
|
||||
await ctx.plugin(ToolFs)
|
||||
},
|
||||
note:
|
||||
'The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-observation-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. `read_image` is not registered without `ctx.attachments`; its schema is route-independent, and execution refuses unless the exact routed model declares image input.',
|
||||
'The read-before-write/edit policy is added by `@deepseek-ai/dsh-fs-observation-policy` (an `fs/*` event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The image tool is not registered without `ctx.attachments`; its schema is route-independent, and execution refuses unless the exact routed model declares image input.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-fs-search',
|
||||
@@ -651,9 +651,9 @@ export function assertManifestComplete(packages: ToolPackage[] = TOOL_PACKAGES,
|
||||
* A tool package that boots without registering anything is a broken boot, not
|
||||
* an empty catalog section. The usual cause is an `inject` the entry's `mount`
|
||||
* does not satisfy: cordis leaves the plugin PENDING, every step here still
|
||||
* succeeds, and the generator writes a catalog missing that package's tools —
|
||||
* with the freshness gate green on it, because the omission is now what the
|
||||
* generator produces. {@link assertManifestComplete} cannot see this: the
|
||||
* succeeds, and the generator writes a catalog missing that package's tools.
|
||||
* The freshness gate stays green because regeneration reproduces the omission.
|
||||
* {@link assertManifestComplete} cannot see this: the
|
||||
* package IS listed, it just contributed nothing.
|
||||
* @param entry - the manifest entry that was booted.
|
||||
* @param harvested - how many schemas its boot registered.
|
||||
@@ -808,7 +808,6 @@ async function main(): Promise<void> {
|
||||
console.log(`gen-tool-catalog: wrote ${OUT}.`)
|
||||
}
|
||||
|
||||
// Run only when invoked as a script, not when imported by a test.
|
||||
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
|
||||
await main()
|
||||
}
|
||||
|
||||
@@ -18,19 +18,19 @@ interface Profile {
|
||||
// snapshot; they do not re-evaluate that deleted baseline or track its preset.
|
||||
const profiles = {
|
||||
source: {
|
||||
count: 88,
|
||||
count: 89,
|
||||
indexes: [0, 1, 4, 5],
|
||||
sha256: 'da1dfd77cb6eb66be93d8d3820f9b9b68b7aa391c24680f8851c0910298f9e3b',
|
||||
sha256: '3e8b8455827254d6da59afb803d30f062b9fe1264b4df75ce4c8cd6e742b93dd',
|
||||
},
|
||||
example: {
|
||||
count: 87,
|
||||
count: 88,
|
||||
indexes: [0, 1, 2, 4, 5],
|
||||
sha256: '6a2606053bc1ec1de3b02611de88ea51d201dac13a1f193e4934d33c08b95f08',
|
||||
sha256: 'e2b944c028ef1971302d47b2eb0e95c05e740623c7cbaebb4b1312dbf73bf156',
|
||||
},
|
||||
test: {
|
||||
count: 83,
|
||||
count: 84,
|
||||
indexes: [0, 3, 4, 5],
|
||||
sha256: '7995e14926a36c40bd65c474637735222a95fb030395681685f03060e50a7b78',
|
||||
sha256: '7800bf57f0d689a5623e4ad60315b10173158fb9cf988d3f005cb115f2245691',
|
||||
},
|
||||
} as const satisfies Record<string, Profile>
|
||||
|
||||
@@ -85,7 +85,7 @@ describe('Oxlint repository rule fingerprint', () => {
|
||||
const overrides: readonly unknown[] = parsed.overrides
|
||||
|
||||
it('pins every override field', () => {
|
||||
expect(overrides).toHaveLength(8)
|
||||
expect(overrides).toHaveLength(9)
|
||||
})
|
||||
|
||||
it.each(Object.entries(profiles))('pins the %s rule profile', (_name, profile) => {
|
||||
|
||||
+20
-3
@@ -177,6 +177,22 @@ function relativeImports(file: string, sourceText: string): RelativeImport[] {
|
||||
return imports
|
||||
}
|
||||
|
||||
/**
|
||||
* publint reads the CommonJS interop preamble inside the prebuilt browser
|
||||
* bundles and reports CJS-written-as-ESM. Node never resolves those files:
|
||||
* `lib/client.js` is evaluated by the page module system as a classic script,
|
||||
* and `lib/worker.js` by `new Worker(url, { type: 'module' })` — both outside
|
||||
* the Node resolution publint models. Exactly that verdict on exactly those
|
||||
* files is suppressed; every other publint error stays fatal.
|
||||
*/
|
||||
function isBrowserBundleFormatFalsePositive(message: Message): boolean {
|
||||
if (message.code !== 'FILE_INVALID_FORMAT') return false
|
||||
const filePath = (message.args as { actualFilePath?: string }).actualFilePath ?? ''
|
||||
const exportKey = Array.isArray(message.path) ? message.path.join('/') : ''
|
||||
return /(^|\/)lib\/(client|worker)\.js$/.test(filePath)
|
||||
|| /(^|\/)\.\/(client|worker)$/.test(exportKey)
|
||||
}
|
||||
|
||||
async function runPublint(target: PackageTarget): Promise<PublintResult> {
|
||||
try {
|
||||
const files = publicationFiles(target)
|
||||
@@ -186,9 +202,10 @@ async function runPublint(target: PackageTarget): Promise<PublintResult> {
|
||||
pack: { files },
|
||||
})
|
||||
const manifest = result.pkg as Record<string, unknown>
|
||||
return result.messages.some(message => message.type === 'error') || closureViolations.length > 0
|
||||
? { path: target.path, status: 'failed', messages: result.messages, closureViolations, manifest }
|
||||
: { path: target.path, status: 'passed', messages: result.messages, closureViolations, manifest }
|
||||
const messages = result.messages.filter(message => !isBrowserBundleFormatFalsePositive(message))
|
||||
return messages.some(message => message.type === 'error') || closureViolations.length > 0
|
||||
? { path: target.path, status: 'failed', messages, closureViolations, manifest }
|
||||
: { path: target.path, status: 'passed', messages, closureViolations, manifest }
|
||||
} catch (error: unknown) {
|
||||
return {
|
||||
path: target.path,
|
||||
|
||||
@@ -40,13 +40,9 @@ const ROOT_MANIFEST = 'package.json'
|
||||
|
||||
/** One manifest the bump rewrites, and the tag its new version will carry. */
|
||||
interface PlannedVersion {
|
||||
/** Repository-relative manifest path. */
|
||||
readonly manifestPath: string
|
||||
/** Label for the log line. */
|
||||
readonly label: string
|
||||
/** The version the manifest currently carries. */
|
||||
readonly from: string
|
||||
/** The version to write. */
|
||||
readonly to: string
|
||||
/** The tag this version publishes from, or undefined for a non-published manifest. */
|
||||
readonly tag: string | undefined
|
||||
|
||||
@@ -38,7 +38,6 @@ const WORKSPACE_ROOT_PACKAGE = '@deepseek-ai/dsh-root'
|
||||
|
||||
/** One peer declaration the publish order leaves unordered. */
|
||||
interface DroppedPeerEdge {
|
||||
/** Package declaring the peer. */
|
||||
readonly consumer: string
|
||||
/** The declared peer, which publishes after `consumer` or alongside it in a cycle. */
|
||||
readonly peer: string
|
||||
@@ -52,7 +51,6 @@ interface DroppedPeerEdge {
|
||||
* log is the only one who can judge whether a newly dropped edge is expected.
|
||||
*/
|
||||
export interface PublishPlan {
|
||||
/** Members in publish order. */
|
||||
readonly order: readonly ReleaseMember[]
|
||||
/** Peer declarations left unordered, in the order the traversal reached them. */
|
||||
readonly droppedPeerEdges: readonly DroppedPeerEdge[]
|
||||
@@ -60,13 +58,9 @@ export interface PublishPlan {
|
||||
|
||||
/** One publishable package of a release family. */
|
||||
export interface ReleaseMember {
|
||||
/** Repository-relative package directory, for example `packages/core/session`. */
|
||||
readonly directory: string
|
||||
/** Package name from its manifest. */
|
||||
readonly name: string
|
||||
/** Package version from its manifest. */
|
||||
readonly version: string
|
||||
/** The parsed manifest, for payload policy and publication checks. */
|
||||
readonly manifest: Readonly<Record<string, unknown>>
|
||||
}
|
||||
|
||||
@@ -98,18 +92,16 @@ function requireString(manifest: Record<string, unknown>, field: string, context
|
||||
|
||||
/** The executable a family's installed artifacts are driven through. */
|
||||
export interface InstalledEntry {
|
||||
/** Package that carries the executable. */
|
||||
readonly packageName: string
|
||||
/** Path to the executable inside that package. */
|
||||
readonly binPath: string
|
||||
}
|
||||
|
||||
/** A release sequence: its members, its version baseline, and its tag naming. */
|
||||
export abstract class ReleaseFamily {
|
||||
/** Workflow-facing identifier, also the `--family` argument. */
|
||||
/** Workflow-facing `--family` identifier. */
|
||||
abstract readonly id: string
|
||||
|
||||
/** Glob patterns, relative to the repository root, that select this family's manifests. */
|
||||
/** Repository-relative glob patterns selecting this family's manifests. */
|
||||
abstract readonly patterns: readonly string[]
|
||||
|
||||
/** Git tag prefix this family publishes from. */
|
||||
|
||||
@@ -9,9 +9,7 @@ import { fileURLToPath } from 'node:url'
|
||||
|
||||
/** Where and with what environment a release step runs a command. */
|
||||
export interface RunOptions {
|
||||
/** Working directory; defaults to the current one. */
|
||||
readonly cwd?: string
|
||||
/** Child environment; defaults to this process's. */
|
||||
readonly env?: NodeJS.ProcessEnv
|
||||
}
|
||||
|
||||
@@ -19,9 +17,7 @@ export interface RunOptions {
|
||||
export interface CommandResult {
|
||||
/** Exit status, or null when a signal ended the process. */
|
||||
readonly status: number | null
|
||||
/** Captured standard output. */
|
||||
readonly stdout: string
|
||||
/** Captured standard error. */
|
||||
readonly stderr: string
|
||||
}
|
||||
|
||||
@@ -39,19 +35,8 @@ export function attempt(command: string, args: readonly string[], options: RunOp
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a command, capture its output, and echo it once the command exits.
|
||||
*
|
||||
* A step that both shows what a command said and classifies its own failure
|
||||
* needs both halves: the output has to reach the workflow log, and the caller has
|
||||
* to read it to decide whether a failure is worth retrying.
|
||||
*
|
||||
* This is not live progress. `spawnSync` returns only after the child exits, so
|
||||
* nothing appears while the command runs, and the two streams are echoed one
|
||||
* after the other — all of stdout, then all of stderr — which loses their
|
||||
* interleaving. For an npm publish that matters in one visible way: `npm notice`
|
||||
* lines go to stderr while the `+ name@version` confirmation goes to stdout, so
|
||||
* the log shows the confirmation first. Live progress would need an
|
||||
* asynchronous spawn with data listeners.
|
||||
* Run a command, then echo and return its captured output. Output is buffered
|
||||
* until exit and stdout precedes stderr.
|
||||
* @param command - executable name.
|
||||
* @param args - command arguments.
|
||||
* @param options - working directory and environment.
|
||||
@@ -62,8 +47,6 @@ export function attemptEchoed(command: string, args: readonly string[], options:
|
||||
cwd: options.cwd,
|
||||
env: options.env,
|
||||
encoding: 'utf8',
|
||||
// 'inherit' would leave nothing to capture, so the streams are piped and
|
||||
// echoed instead.
|
||||
stdio: ['inherit', 'pipe', 'pipe'],
|
||||
})
|
||||
if (result.error !== undefined) throw result.error
|
||||
@@ -101,11 +84,7 @@ export function run(command: string, args: readonly string[], options: RunOption
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this module is the process entry point.
|
||||
*
|
||||
* The release scripts are both commands and modules: a test imports their pure
|
||||
* logic, and importing a module runs its body, so an unguarded `main()` would
|
||||
* run the wrong command with the wrong arguments.
|
||||
* Return whether Node started the given module as the process entry point.
|
||||
* @param moduleUrl - the caller's `import.meta.url`.
|
||||
* @returns True when Node started this module.
|
||||
*/
|
||||
|
||||
@@ -434,8 +434,8 @@ const VENDORED_LIBRARY = /^@deepseek-ai\\/(cosmokit|schemastery)(\\/|$)/
|
||||
expect: 1,
|
||||
},
|
||||
{
|
||||
// The framework peer is no longer a registry name, so the rehearsal must install this
|
||||
// repository's vendored copies; cosmokit comes along as cordis's own dependency.
|
||||
// The framework peer is a rescoped package, so the rehearsal installs this
|
||||
// repository's vendored copies; cosmokit arrives as cordis's dependency.
|
||||
id: 'packed-install-vendored-peer',
|
||||
file: 'packages/sandbox/sandbox-local/tests/packed-install.e2e.ts',
|
||||
find: ` 'packages/runtime-diagnostics/invariants',
|
||||
@@ -552,7 +552,7 @@ function rewriteLine(line: string, file: string, all: readonly Pattern[]): strin
|
||||
* Markdown splits in two. Every fence is code a reader copies or a
|
||||
* configuration they mount, so every fence follows the rename regardless of its
|
||||
* info string. Prose follows it only under `docs/`, where a sentence quoting
|
||||
* `` `cordis` `` teaches a name this repository no longer resolves; elsewhere
|
||||
* `` `cordis` `` teaches an unresolved package name; elsewhere
|
||||
* prose is a record of what was true when it was written, and the same spelling
|
||||
* can mean something else entirely — the Python SDK's `cordis` option, or the
|
||||
* unvendored `@cordisjs/plugin-http`.
|
||||
|
||||
@@ -180,9 +180,9 @@ for line in sys.stdin:
|
||||
},
|
||||
})
|
||||
elif method == "tools/list":
|
||||
# Keep discovery pending longer than the old smoke's 100 ms grace
|
||||
# period. An SDK runtime that answers initialize too early will make
|
||||
# its first model request without this tool and fail deterministically.
|
||||
# Keep discovery pending long enough that an SDK runtime answering
|
||||
# initialize before discovery completes makes its first model request
|
||||
# without this tool and fails deterministically.
|
||||
time.sleep(0.25)
|
||||
send({
|
||||
"jsonrpc": "2.0",
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": "# DeepSeek Harness\n\nEnglish | [中文](README.zh.md)\n\nDeepSeek Harness (`dsh`) is an open-source agent harness developed by [DeepSeek AI](https://deepseek.com).\n\nIt uses an architecture where **everything is a plugin**, and is powered by [Cordis](https://github.com/cordiverse/cordis), whose design is described in [_A Programming Paradigm for Spatiotemporal Composability_](https://github.com/cordiverse/paper).\n\n## Developer preview\n\nDeepSeek Harness is currently in _developer preview_ and is iterating rapidly. **THERE WILL BE COMPATIBILITY-BREAKING CHANGES.**\n\n## Run\n\n### Run from `npm`\n\nInstall `Node.js`, then run:\n\n```sh\nnpx @deepseek-ai/dsh web\n```\n\nThe command starts the Web UI at `http://127.0.0.1:3080` by default and opens it in the default browser for a local launch. An SSH launch only prints the host URL because the SSH client or editor owns the local forwarded address. Pass `--no-open` to run the server without opening a browser. See [Web UI guide](docs/user/guide/index.md).\n\n### Run from source\n\nTo run from a repository checkout:\n\n```sh\ngit clone https://github.com/deepseek-ai/deepseek-harness.git\ncd deepseek-harness\npnpm install\npnpm run build\npnpm dsh web\n```\n\n`pnpm run build` prepares the repository artifacts. `pnpm dsh web` uses those built artifacts without rebuilding.\n\n## Community and support\n\n- Feel free to submit feedback or bug reports through [GitHub Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions).\n- Add the [`dsh-plugin`](https://github.com/topics/dsh-plugin) topic to your plugin repository for discoverability.\n- Join <a href=\"https://discord.gg/Ycq5dCaS4\">DeepSeek Harness Discord community</a>.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Development\n\nStart with the [development guide](docs/development.md) and [architecture documentation](docs/architecture.md).\n\nFor agents, follow [AGENTS.md](AGENTS.md).\n\n## License\n\n[MIT](LICENSE)\n\nThird-party dependencies and their licenses are disclosed in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).\n"
|
||||
"content": "# DeepSeek Harness\n\nEnglish | [中文](README.zh.md)\n\nDeepSeek Harness (`dsh`) is an open-source agent harness developed by [DeepSeek AI](https://deepseek.com).\n\nIt uses an architecture where **everything is a plugin**, and is powered by [Cordis](https://github.com/cordiverse/cordis), whose design is described in [_A Programming Paradigm for Spatiotemporal Composability_](https://github.com/cordiverse/paper).\n\nDocumentation: [https://deepseek-harness.github.io/deepseek-harness/](https://deepseek-harness.github.io/deepseek-harness/)\n\n## Developer preview\n\nDeepSeek Harness is currently in _developer preview_ and is iterating rapidly. **THERE WILL BE COMPATIBILITY-BREAKING CHANGES.**\n\n## Run\n\n### Run from `npm`\n\nInstall `Node.js`, then run:\n\n```sh\nnpx @deepseek-ai/dsh web\n```\n\nThe command starts the Web UI at `http://127.0.0.1:3080` by default and opens it in the default browser for a local launch. An SSH launch only prints the host URL because the SSH client or editor owns the local forwarded address. Pass `--no-open` to run the server without opening a browser. See [Web UI guide](docs/user/guide/index.md).\n\n### Run from source\n\nTo run from a repository checkout:\n\n```sh\ngit clone https://github.com/deepseek-ai/deepseek-harness.git\ncd deepseek-harness\npnpm install\npnpm run build\npnpm dsh web\n```\n\n`pnpm run build` prepares the repository artifacts. `pnpm dsh web` uses those built artifacts without rebuilding.\n\n## Community and support\n\n- Feel free to submit feedback or bug reports through [GitHub Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions).\n- Add the [`dsh-plugin`](https://github.com/topics/dsh-plugin) topic to your plugin repository for discoverability.\n- Join <a href=\"https://discord.gg/Ycq5dCaS4\">DeepSeek Harness Discord community</a>.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Development\n\nStart with the [development guide](docs/development.md) and [architecture documentation](docs/architecture.md).\n\nFor agents, follow [AGENTS.md](AGENTS.md).\n\n## License\n\n[MIT](LICENSE)\n\nThird-party dependencies and their licenses are disclosed in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).\n"
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "# DeepSeek Harness\n\n[English](README.md) | 中文\n\nDeepSeek Harness(`dsh`)是由 [DeepSeek AI](https://deepseek.com) 开发的开源 agent harness(智能体框架)。\n\n它采用**一切皆插件**的架构,并由 [Cordis](https://github.com/cordiverse/cordis) 驱动,其设计参见论文 [_A Programming Paradigm for Spatiotemporal Composability_](https://github.com/cordiverse/paper)。\n\n## 开发者预览\n\nDeepSeek Harness 目前处于 _开发者预览_ 阶段,正在快速迭代。**未来将出现破坏兼容性的变更。**\n\n<a id=\"run\"></a>\n\n## 运行\n\n### 通过 `npm` 运行\n\n安装 `Node.js`,然后运行:\n\n```sh\nnpx @deepseek-ai/dsh web\n```\n\n该命令默认会在 `http://127.0.0.1:3080` 启动 Web UI,本机启动时还会用默认浏览器打开页面。通过 SSH 启动时只打印宿主机 URL,因为本地转发地址由 SSH 客户端或编辑器持有。传入 `--no-open` 可仅运行服务器而不打开浏览器。详见 [Web UI 指南](docs/user/guide/index.zh.md)。\n\n<a id=\"run-from-source\"></a>\n\n### 从源码运行\n\n如需从仓库源码运行:\n\n```sh\ngit clone https://github.com/deepseek-ai/deepseek-harness.git\ncd deepseek-harness\npnpm install\npnpm run build\npnpm dsh web\n```\n\n`pnpm run build` 会准备仓库产物。`pnpm dsh web` 会直接使用这些已构建产物,不会重新构建。\n\n## 社区与支持\n\n- 欢迎通过 [GitHub Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions) 提交反馈或 bug 报告。\n- 为你的插件仓库添加 [`dsh-plugin`](https://github.com/topics/dsh-plugin) 话题,便于被发现。\n- 欢迎加入 DeepSeek Harness 企微群:扫码添加企微小助手并填写入群问卷,完成后小助手会邀请你入群。\n\n<table>\n <thead>\n <tr>\n <th align=\"center\">企微小助手</th>\n <th align=\"center\">入群问卷</th>\n <th align=\"center\">微信公众号</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td align=\"center\"><img src=\"https://cdn.deepseek.com/harness/readme/community-wecom-assistant.png\" alt=\"DeepSeek Harness 企微小助手二维码\" width=\"180\" height=\"180\"></td>\n <td align=\"center\"><a href=\"https://trtgsjkv6r.feishu.cn/share/base/form/shrcnIt5twSVdLGD52KJBckGCgg\"><img src=\"https://cdn.deepseek.com/harness/readme/community-wecom-survey.png\" alt=\"DeepSeek Harness 入群问卷二维码\" width=\"180\" height=\"180\"></a></td>\n <td align=\"center\"><img src=\"https://cdn.deepseek.com/harness/readme/community-wechat-official-account.png\" alt=\"DeepSeek Harness 团队微信公众号二维码\" width=\"180\" height=\"180\"></td>\n </tr>\n </tbody>\n</table>\n\n## 参与贡献\n\n参见 [CONTRIBUTING.md](CONTRIBUTING.zh.md)。\n\n## 开发\n\n请先阅读[开发指南](docs/development.zh.md)与[架构文档](docs/architecture.zh.md)。\n\n面向 agent:请遵循 [AGENTS.md](AGENTS.md)。\n\n## 许可证\n\n[MIT](LICENSE)\n\n第三方依赖及其许可证见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。\n"
|
||||
"content": "# DeepSeek Harness\n\n[English](README.md) | 中文\n\nDeepSeek Harness(`dsh`)是由 [DeepSeek AI](https://deepseek.com) 开发的开源 agent harness(智能体框架)。\n\n它采用**一切皆插件**的架构,并由 [Cordis](https://github.com/cordiverse/cordis) 驱动,其设计参见论文 [_A Programming Paradigm for Spatiotemporal Composability_](https://github.com/cordiverse/paper)。\n\n文档:[https://deepseek-harness.github.io/deepseek-harness/](https://deepseek-harness.github.io/deepseek-harness/)\n\n## 开发者预览\n\nDeepSeek Harness 目前处于 _开发者预览_ 阶段,正在快速迭代。**未来将出现破坏兼容性的变更。**\n\n<a id=\"run\"></a>\n\n## 运行\n\n### 通过 `npm` 运行\n\n安装 `Node.js`,然后运行:\n\n```sh\nnpx @deepseek-ai/dsh web\n```\n\n该命令默认会在 `http://127.0.0.1:3080` 启动 Web UI,本机启动时还会用默认浏览器打开页面。通过 SSH 启动时只打印宿主机 URL,因为本地转发地址由 SSH 客户端或编辑器持有。传入 `--no-open` 可仅运行服务器而不打开浏览器。详见 [Web UI 指南](docs/user/guide/index.zh.md)。\n\n<a id=\"run-from-source\"></a>\n\n### 从源码运行\n\n如需从仓库源码运行:\n\n```sh\ngit clone https://github.com/deepseek-ai/deepseek-harness.git\ncd deepseek-harness\npnpm install\npnpm run build\npnpm dsh web\n```\n\n`pnpm run build` 会准备仓库产物。`pnpm dsh web` 会直接使用这些已构建产物,不会重新构建。\n\n## 社区与支持\n\n- 欢迎通过 [GitHub Discussions](https://github.com/deepseek-ai/deepseek-harness/discussions) 提交反馈或 bug 报告。\n- 为你的插件仓库添加 [`dsh-plugin`](https://github.com/topics/dsh-plugin) 话题,便于被发现。\n- 欢迎加入 DeepSeek Harness 企微群:扫码添加企微小助手并填写入群问卷,完成后小助手会邀请你入群。\n\n<table>\n <thead>\n <tr>\n <th align=\"center\">企微小助手</th>\n <th align=\"center\">入群问卷</th>\n <th align=\"center\">微信公众号</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td align=\"center\"><img src=\"https://cdn.deepseek.com/harness/readme/community-wecom-assistant.png\" alt=\"DeepSeek Harness 企微小助手二维码\" width=\"180\" height=\"180\"></td>\n <td align=\"center\"><a href=\"https://trtgsjkv6r.feishu.cn/share/base/form/shrcnIt5twSVdLGD52KJBckGCgg\"><img src=\"https://cdn.deepseek.com/harness/readme/community-wecom-survey.png\" alt=\"DeepSeek Harness 入群问卷二维码\" width=\"180\" height=\"180\"></a></td>\n <td align=\"center\"><img src=\"https://cdn.deepseek.com/harness/readme/community-wechat-official-account.png\" alt=\"DeepSeek Harness 团队微信公众号二维码\" width=\"180\" height=\"180\"></td>\n </tr>\n </tbody>\n</table>\n\n## 参与贡献\n\n参见 [CONTRIBUTING.md](CONTRIBUTING.zh.md)。\n\n## 开发\n\n请先阅读[开发指南](docs/development.zh.md)与[架构文档](docs/architecture.zh.md)。\n\n面向 agent:请遵循 [AGENTS.md](AGENTS.md)。\n\n## 许可证\n\n[MIT](LICENSE)\n\n第三方依赖及其许可证见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。\n"
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
|
||||
@@ -42,9 +42,7 @@ export function runGit(root: string, args: string[], operation: string, input?:
|
||||
|
||||
/** One regular stage-zero Git index entry and its exact blob bytes. */
|
||||
export interface GitIndexBlob {
|
||||
/** Object ID recorded in the index. */
|
||||
objectId: string
|
||||
/** Blob bytes stored under that object ID. */
|
||||
content: Buffer
|
||||
}
|
||||
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"symbol": "SessionLocation",
|
||||
"source": "packages/session/session-persistence/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/persistence.md",
|
||||
"symbol": "SessionFormatMigration",
|
||||
"source": "packages/session/session-persistence/src/format-decoder.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/persistence.md",
|
||||
"symbol": "SessionRawArtifact",
|
||||
@@ -930,6 +935,16 @@
|
||||
"symbol": "StoredImageAttachment",
|
||||
"source": "packages/attachment/attachment/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/attachment.md",
|
||||
"symbol": "ImageRequestPolicy",
|
||||
"source": "packages/attachment/attachment/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/attachment.md",
|
||||
"symbol": "RequestImageAttachment",
|
||||
"source": "packages/attachment/attachment/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/shell.md",
|
||||
"symbol": "ShellExecRequest",
|
||||
|
||||
@@ -22,13 +22,9 @@ const CLIENT_WEB = '@deepseek-ai/dsh-client-web'
|
||||
|
||||
/** One workspace package's browser-module declaration. */
|
||||
export interface ClientDeclaration {
|
||||
/** npm package name. */
|
||||
readonly name: string
|
||||
/** Repository-relative package manifest. */
|
||||
readonly manifest: string
|
||||
/** Whether the manifest declares a dynamic dsh.client row. */
|
||||
readonly dynamic: boolean
|
||||
/** Exact module-table specifiers requested by the row. */
|
||||
readonly external: readonly string[]
|
||||
/** Informational package dependencies declared by the row. */
|
||||
readonly inject: readonly string[]
|
||||
@@ -36,43 +32,28 @@ export interface ClientDeclaration {
|
||||
|
||||
/** One package directly under packages/client. */
|
||||
export interface ClientPackage extends ClientDeclaration {
|
||||
/** Whether its build config uses the staticLinked preset. */
|
||||
readonly staticLinked: boolean
|
||||
/** Production source locations grouped by imported package name. */
|
||||
readonly sourceUses: Readonly<Record<string, readonly string[]>>
|
||||
/** Production source locations grouped by runtime-imported package name. */
|
||||
readonly runtimeSourceUses: Readonly<Record<string, readonly string[]>>
|
||||
/** Installed implementation dependencies. */
|
||||
readonly dependencies: Readonly<Record<string, string>>
|
||||
/** Consumer-supplied dependencies. */
|
||||
readonly peerDependencies: Readonly<Record<string, string>>
|
||||
/** Dependencies available while developing the package. */
|
||||
readonly devDependencies: Readonly<Record<string, string>>
|
||||
}
|
||||
|
||||
/** Complete source-plane input to the client package verifier. */
|
||||
export interface ClientPackageFacts {
|
||||
/** Packages directly under packages/client. */
|
||||
readonly packages: readonly ClientPackage[]
|
||||
/** Every workspace package, including packages without a browser row. */
|
||||
readonly declarations: readonly ClientDeclaration[]
|
||||
/** Packages whose build config uses the staticLinked preset. */
|
||||
readonly staticLinkedPackages: ReadonlySet<string>
|
||||
/** Specifiers the web shell seeds into the module table. */
|
||||
readonly platformModules: readonly string[]
|
||||
/** Dynamic factories the HTML parser loads before shell boot. */
|
||||
readonly preloadedExternals: readonly string[]
|
||||
/** Package rows whose bundles the HTML parser executes before shell boot. */
|
||||
readonly parserPreloadIds: readonly string[]
|
||||
/** Manifest field errors found while reading declarations. */
|
||||
readonly malformed: readonly string[]
|
||||
}
|
||||
|
||||
/** Result of reading every workspace browser-module declaration. */
|
||||
export interface ClientDeclarations {
|
||||
/** One declaration record per named workspace manifest. */
|
||||
readonly declarations: ClientDeclaration[]
|
||||
/** Manifest field errors that prevent a reliable declaration. */
|
||||
readonly malformed: string[]
|
||||
}
|
||||
|
||||
|
||||
@@ -22,37 +22,28 @@ const PROTOCOL_EXPORTS = new Set(['Config', 'inject', 'name', 'reusable', 'apply
|
||||
|
||||
/** Per-file walk state threaded through the scope recursion. */
|
||||
interface Walk {
|
||||
/** Repo-relative path of the file being walked. */
|
||||
rel: string
|
||||
/** The parsed source file. */
|
||||
sf: ts.SourceFile
|
||||
/** Raw file text (rawJsDoc reads comment ranges out of it). */
|
||||
text: string
|
||||
/** The program's checker, consulted only for heritage-member lookups. */
|
||||
checker: ts.TypeChecker
|
||||
/** The aggregate violation list, appended in place. */
|
||||
violations: string[]
|
||||
}
|
||||
|
||||
/** True when a statement carries the `export` modifier. */
|
||||
function isExported(stmt: ts.Statement): boolean {
|
||||
return ts.canHaveModifiers(stmt) && (ts.getModifiers(stmt)?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) ?? false)
|
||||
}
|
||||
|
||||
/** True for a class member a consumer cannot reach: `private`/`protected`/`#name`. */
|
||||
function isNonPublic(member: ts.ClassElement): boolean {
|
||||
const mods = ts.canHaveModifiers(member) ? ts.getModifiers(member) : undefined
|
||||
return (mods?.some(m => m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword) ?? false)
|
||||
|| ('name' in member && ts.isPrivateIdentifier(member.name))
|
||||
}
|
||||
|
||||
/** True when a class member carries the `static` modifier. */
|
||||
function isStatic(member: ts.ClassElement): boolean {
|
||||
const mods = ts.canHaveModifiers(member) ? ts.getModifiers(member) : undefined
|
||||
return mods?.some(m => m.kind === ts.SyntaxKind.StaticKeyword) ?? false
|
||||
}
|
||||
|
||||
/** The `this`-receiver exemption every function-like check shares. */
|
||||
function thisReceiver(p: ts.ParameterDeclaration): boolean {
|
||||
return ts.isIdentifier(p.name) && p.name.text === 'this'
|
||||
}
|
||||
@@ -611,7 +602,6 @@ function main(): void {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Run only when invoked as a script, not when imported by a test.
|
||||
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
|
||||
main()
|
||||
}
|
||||
|
||||
@@ -195,7 +195,6 @@ export function findViolations(
|
||||
return out
|
||||
}
|
||||
|
||||
// Run only when invoked as a script, not when imported by the spec.
|
||||
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
|
||||
// Archived notes remain valid link targets, but their historical outbound links are frozen.
|
||||
const files = uniqueRepoFiles(root, PATTERNS, isArchivedAgentNotePath)
|
||||
|
||||
@@ -208,7 +208,6 @@ function main(): void {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Run only when invoked as a script, not when imported by a test.
|
||||
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
|
||||
main()
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/code-runtime/code-runtime-worker-thread': { kind: 'indirect', reason: 'The worker backend delegates model rendering to Code Mode in dsh-tools.' },
|
||||
'packages/code-runtime/code-runtime-python': { kind: 'indirect', reason: 'The CPython subprocess backend delegates model rendering to Code Mode in dsh-tools.' },
|
||||
'packages/client/ui-agent-preset': { kind: 'indirect', reason: 'Browser-side settings row; the preset it selects owns every model-facing effect.' },
|
||||
'packages/util/crypto': { kind: 'indirect', reason: 'Pure identifier minting; the ids consumers mint with it never enter prompts as semantic content.' },
|
||||
'packages/core/agent-default-model': { kind: 'indirect', reason: 'The service supplies a ModelSelection; request assembly and adapters own the model-visible request.' },
|
||||
'packages/preset/agent-presets': { kind: 'indirect', reason: 'The mount installs a preset\'s own plugins, which own every model-facing registration it makes visible.' },
|
||||
'packages/typert/registry': { kind: 'none', reason: 'Runtime type registry; consumers (cordis_inspect, wire faces, gates) own any model-visible projection of registry contents.' },
|
||||
@@ -61,6 +62,8 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/client/hmr': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/modules': { kind: 'none', reason: 'Browser-side module-loading kernel machinery; registers nothing model-facing.' },
|
||||
'packages/test-support/client-runtime': { kind: 'none', reason: 'Browser-side test infrastructure (jsdom bench); registers nothing model-facing.' },
|
||||
'packages/experimental/webworker-runtime': { kind: 'none', reason: 'Browser-side host runtime and Node-compatibility layer; the plugins it boots own every model-facing registration.' },
|
||||
'packages/experimental/webworker-packer': { kind: 'none', reason: 'Build-time image writer; its output reaches a model only through the tree the worker then boots.' },
|
||||
'packages/client/ui-slots': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-attachment': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-primitives': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
|
||||
Reference in New Issue
Block a user