diff --git a/.changeset/brave-pandas-document.md b/.changeset/brave-pandas-document.md
new file mode 100644
index 000000000..18fcfdd70
--- /dev/null
+++ b/.changeset/brave-pandas-document.md
@@ -0,0 +1,6 @@
+---
+"@upstash/context7-deepseek-harness": minor
+"ctx7": minor
+---
+
+Add the official Context7 plugin for DeepSeek Harness with native library resolution, documentation tools, credential-provider integration, and one-command CLI setup.
diff --git a/README.md b/README.md
index aa34be9ba..196c01b0e 100644
--- a/README.md
+++ b/README.md
@@ -127,6 +127,7 @@ Always use Context7 when I need library/API documentation, code generation, setu
- [`ctx7`](https://www.npmjs.com/package/ctx7) - CLI
- [`@upstash/context7-sdk`](https://www.npmjs.com/package/@upstash/context7-sdk) - TypeScript SDK
- [`@upstash/context7-tools-ai-sdk`](https://www.npmjs.com/package/@upstash/context7-tools-ai-sdk) - Vercel AI SDK tools
+- [`@upstash/context7-deepseek-harness`](https://www.npmjs.com/package/@upstash/context7-deepseek-harness) - DeepSeek Harness plugin
- [`@upstash/context7-pi`](https://www.npmjs.com/package/@upstash/context7-pi) - pi.dev extension
## Disclaimer
diff --git a/docs/clients/deepseek-harness.mdx b/docs/clients/deepseek-harness.mdx
new file mode 100644
index 000000000..b841554ca
--- /dev/null
+++ b/docs/clients/deepseek-harness.mdx
@@ -0,0 +1,79 @@
+---
+title: DeepSeek Harness
+description: Using Context7 with DeepSeek Harness
+---
+
+Context7 integrates with [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) through the official [`@upstash/context7-deepseek-harness`](https://github.com/upstash/context7/tree/master/packages/deepseek-harness) plugin. It registers Context7's documentation tools natively through the harness tool service and adds system-prompt guidance so library-specific questions invoke them automatically.
+
+## Installation
+
+Install the bundle into a DeepSeek Harness profile and store a Context7 API key in the Harness credential provider:
+
+```bash
+npx ctx7@latest setup --deepseek headless
+```
+
+Pass another profile name instead of `headless` when needed. Without an existing Context7 login, setup starts the device authorization flow and creates an API key.
+
+To use anonymous Context7 limits without configuring a key, install the bundle directly:
+
+```bash
+dsh plugin --profile headless add @upstash/context7-deepseek-harness
+```
+
+Verify the composed configuration and start the profile:
+
+```bash
+dsh --profile headless --dump-config
+dsh --profile headless
+```
+
+Remove it with `dsh plugin --profile headless remove @upstash/context7-deepseek-harness`.
+
+## Authentication
+
+The plugin resolves `CONTEXT7_API_KEY` through DeepSeek Harness's credential provider before every request. The setup command stores it in `$DSH_HOME/.credentials.yaml`, which defaults to `~/.dsh/.credentials.yaml`. Credential changes apply without reloading the plugin.
+
+The launching environment remains supported:
+
+```bash
+export CONTEXT7_API_KEY=ctx7sk_...
+```
+
+For manual credential-file setup, edit the flat YAML mapping and apply owner-only permissions:
+
+```bash
+mkdir -p ~/.dsh
+chmod 700 ~/.dsh
+${EDITOR:-vi} ~/.dsh/.credentials.yaml
+chmod 600 ~/.dsh/.credentials.yaml
+```
+
+```yaml
+CONTEXT7_API_KEY: ctx7sk_...
+```
+
+Never put API keys in `cordis.yml` or `cordis.patch.yml`; composed configuration can be printed and shared during debugging.
+
+## What it adds
+
+
+
+Finds Context7-compatible library IDs and available versions for a package or product.
+
+
+Fetches current documentation and code examples for a selected library ID.
+
+
+
+## Usage
+
+Ask a documentation question in a DeepSeek Harness session:
+
+```
+How do I configure caching in Next.js 16?
+Show me the current Prisma syntax for relations.
+What are the Supabase authentication methods?
+```
+
+The model resolves the relevant Context7 library ID and then queries its documentation.
diff --git a/docs/docs.json b/docs/docs.json
index bd4faa07d..1bd612f1e 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -156,6 +156,7 @@
"clients/codex",
"clients/copilot-cli",
"clients/cursor",
+ "clients/deepseek-harness",
"clients/opencode",
"clients/pi",
"clients/vscode",
diff --git a/packages/cli/package.json b/packages/cli/package.json
index d6c7aa652..ab44a4257 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -29,7 +29,8 @@
"figlet": "^1.9.4",
"open": "^10.1.0",
"ora": "^9.4.0",
- "picocolors": "^1.1.1"
+ "picocolors": "^1.1.1",
+ "yaml": "^2.9.0"
},
"devDependencies": {
"@types/figlet": "^1.7.0",
diff --git a/packages/cli/src/__tests__/deepseek-command.test.ts b/packages/cli/src/__tests__/deepseek-command.test.ts
new file mode 100644
index 000000000..3abe6768d
--- /dev/null
+++ b/packages/cli/src/__tests__/deepseek-command.test.ts
@@ -0,0 +1,104 @@
+import { Command } from "commander";
+import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
+
+const writeDeepSeekCredential = vi.fn();
+const installDeepSeekPlugin = vi.fn();
+const validateDeepSeekProfile = vi.fn((profile: string) => profile);
+const trackEvent = vi.fn();
+
+vi.mock("../setup/deepseek.js", () => ({
+ writeDeepSeekCredential: (...args: unknown[]) => writeDeepSeekCredential(...args),
+ installDeepSeekPlugin: (...args: unknown[]) => installDeepSeekPlugin(...args),
+ validateDeepSeekProfile: (...args: [string]) => validateDeepSeekProfile(...args),
+}));
+
+vi.mock("../utils/tracking.js", () => ({
+ trackEvent: (...args: unknown[]) => trackEvent(...args),
+}));
+
+const spinner = {
+ start: vi.fn().mockReturnThis(),
+ succeed: vi.fn().mockReturnThis(),
+ fail: vi.fn().mockReturnThis(),
+};
+
+vi.mock("ora", () => ({ default: () => spinner }));
+
+import { registerSetupCommand } from "../commands/setup.js";
+
+async function runSetup(...args: string[]): Promise {
+ const program = new Command();
+ program.exitOverride();
+ registerSetupCommand(program);
+ await program.parseAsync(["node", "test", "setup", ...args]);
+}
+
+beforeEach(() => {
+ vi.clearAllMocks();
+ writeDeepSeekCredential.mockResolvedValue("/tmp/dsh/.credentials.yaml");
+ installDeepSeekPlugin.mockResolvedValue(undefined);
+ validateDeepSeekProfile.mockImplementation((profile: string) => profile);
+ vi.spyOn(console, "log").mockImplementation(() => {});
+ vi.spyOn(console, "error").mockImplementation(() => {});
+ process.exitCode = undefined;
+});
+
+afterEach(() => {
+ vi.restoreAllMocks();
+ process.exitCode = undefined;
+});
+
+describe("DeepSeek Harness setup command", () => {
+ test("stores the credential and installs the requested profile", async () => {
+ await runSetup("--deepseek", "team", "--api-key", "ctx7sk-test");
+
+ expect(writeDeepSeekCredential).toHaveBeenCalledWith("ctx7sk-test");
+ expect(installDeepSeekPlugin).toHaveBeenCalledWith("team");
+ expect(trackEvent).toHaveBeenCalledWith("setup", { mode: "deepseek", profile: "team" });
+ expect(process.exitCode).toBeUndefined();
+ });
+
+ test("uses the headless profile by default", async () => {
+ await runSetup("--deepseek", "--api-key", "ctx7sk-test");
+
+ expect(installDeepSeekPlugin).toHaveBeenCalledWith("headless");
+ });
+
+ test("rejects incompatible setup modes", async () => {
+ await runSetup("--deepseek", "--mcp", "--api-key", "ctx7sk-test");
+
+ expect(writeDeepSeekCredential).not.toHaveBeenCalled();
+ expect(installDeepSeekPlugin).not.toHaveBeenCalled();
+ expect(process.exitCode).toBe(1);
+ });
+
+ test("validates the profile before storing a credential", async () => {
+ validateDeepSeekProfile.mockImplementation(() => {
+ throw new Error("Invalid DeepSeek Harness profile name");
+ });
+
+ await runSetup("--deepseek", "..", "--api-key", "ctx7sk-test");
+
+ expect(writeDeepSeekCredential).not.toHaveBeenCalled();
+ expect(installDeepSeekPlugin).not.toHaveBeenCalled();
+ expect(process.exitCode).toBe(1);
+ });
+
+ test("fails when the credential cannot be written", async () => {
+ writeDeepSeekCredential.mockRejectedValue(new Error("write failed"));
+
+ await runSetup("--deepseek", "--api-key", "ctx7sk-test");
+
+ expect(installDeepSeekPlugin).not.toHaveBeenCalled();
+ expect(process.exitCode).toBe(1);
+ });
+
+ test("fails when the plugin cannot be installed", async () => {
+ installDeepSeekPlugin.mockRejectedValue(new Error("install failed"));
+
+ await runSetup("--deepseek", "--api-key", "ctx7sk-test");
+
+ expect(writeDeepSeekCredential).toHaveBeenCalledWith("ctx7sk-test");
+ expect(process.exitCode).toBe(1);
+ });
+});
diff --git a/packages/cli/src/__tests__/deepseek.test.ts b/packages/cli/src/__tests__/deepseek.test.ts
new file mode 100644
index 000000000..a76f16aaf
--- /dev/null
+++ b/packages/cli/src/__tests__/deepseek.test.ts
@@ -0,0 +1,150 @@
+import { mkdir, mkdtemp, readFile, rm, stat, unlink, writeFile } from "node:fs/promises";
+import { tmpdir } from "node:os";
+import { join, resolve } from "node:path";
+import { parseDocument } from "yaml";
+import { afterEach, describe, expect, test } from "vitest";
+import {
+ CONTEXT7_CREDENTIAL_REF,
+ DEEPSEEK_PLUGIN_PACKAGE,
+ deepSeekPluginInvocation,
+ resolveDshHome,
+ validateDeepSeekProfile,
+ writeDeepSeekCredential,
+} from "../setup/deepseek.js";
+
+const temporaryRoots: string[] = [];
+
+async function temporaryRoot(): Promise {
+ const root = await mkdtemp(join(tmpdir(), "ctx7-deepseek-setup-"));
+ temporaryRoots.push(root);
+ return root;
+}
+
+afterEach(async () => {
+ await Promise.all(
+ temporaryRoots.splice(0).map((root) => rm(root, { recursive: true, force: true }))
+ );
+});
+
+describe("DeepSeek Harness setup", () => {
+ test("resolves the Harness home", () => {
+ const workspace = resolve("workspace");
+ const userHome = join(workspace, "users", "test");
+ expect(
+ resolveDshHome({ DSH_HOME: join(workspace, "custom", "..", "dsh") }, userHome, workspace)
+ ).toBe(join(workspace, "dsh"));
+ expect(resolveDshHome({}, userHome, workspace)).toBe(join(userHome, ".dsh"));
+ expect(resolveDshHome({ DSH_HOME: " " }, userHome, workspace)).toBe(join(userHome, ".dsh"));
+ expect(resolveDshHome({ DSH_HOME: "~/.harness" }, userHome, workspace)).toBe(
+ join(userHome, ".harness")
+ );
+ expect(resolveDshHome({ DSH_HOME: join("state", "dsh") }, userHome, workspace)).toBe(
+ join(workspace, "state", "dsh")
+ );
+ });
+
+ test("writes the credential without replacing other entries or comments", async () => {
+ const home = join(await temporaryRoot(), ".dsh");
+ const filename = join(home, ".credentials.yaml");
+ await mkdir(home, { recursive: true, mode: 0o755 });
+ await writeFile(
+ filename,
+ "# existing credential\nDEEPSEEK_API_KEY: sk-deepseek\nCONTEXT7_API_KEY: old\n",
+ { mode: 0o644 }
+ );
+
+ await expect(writeDeepSeekCredential("ctx7sk-new", home)).resolves.toBe(filename);
+
+ const content = await readFile(filename, "utf8");
+ const values = parseDocument(content).toJS() as Record;
+ expect(content).toContain("# existing credential");
+ expect(values).toEqual({
+ DEEPSEEK_API_KEY: "sk-deepseek",
+ CONTEXT7_API_KEY: "ctx7sk-new",
+ });
+ if (process.platform !== "win32") {
+ expect((await stat(home)).mode & 0o777).toBe(0o700);
+ expect((await stat(filename)).mode & 0o777).toBe(0o600);
+ }
+ });
+
+ test("refuses to overwrite an invalid credential document", async () => {
+ const home = join(await temporaryRoot(), ".dsh");
+ const filename = join(home, ".credentials.yaml");
+ await mkdir(home, { recursive: true });
+ await writeFile(filename, "DEEPSEEK_API_KEY: 42\n", "utf8");
+
+ await expect(writeDeepSeekCredential("ctx7sk-new", home)).rejects.toThrow(
+ 'Credential "DEEPSEEK_API_KEY"'
+ );
+ await expect(readFile(filename, "utf8")).resolves.toBe("DEEPSEEK_API_KEY: 42\n");
+ });
+
+ test("folds in a credential update made by another locked writer", async () => {
+ const home = join(await temporaryRoot(), ".dsh");
+ const filename = join(home, ".credentials.yaml");
+ await mkdir(home, { recursive: true, mode: 0o700 });
+ await writeFile(filename, "DEEPSEEK_API_KEY: first\n", { mode: 0o600 });
+ await writeFile(`${filename}.lock`, "other-writer\n", { mode: 0o600, flag: "wx" });
+
+ const pending = writeDeepSeekCredential("ctx7sk-new", home);
+ await new Promise((resolve) => setTimeout(resolve, 40));
+ await writeFile(filename, "DEEPSEEK_API_KEY: second\nOTHER_API_KEY: preserved\n", {
+ mode: 0o600,
+ });
+ await unlink(`${filename}.lock`);
+ await pending;
+
+ expect(parseDocument(await readFile(filename, "utf8")).toJS()).toEqual({
+ DEEPSEEK_API_KEY: "second",
+ OTHER_API_KEY: "preserved",
+ CONTEXT7_API_KEY: "ctx7sk-new",
+ });
+ });
+
+ test("builds the profile plugin installation command", () => {
+ expect(deepSeekPluginInvocation("headless")).toEqual({
+ command: "npx",
+ args: [
+ "--yes",
+ "@deepseek-ai/dsh",
+ "plugin",
+ "--profile",
+ "headless",
+ "add",
+ DEEPSEEK_PLUGIN_PACKAGE,
+ ],
+ });
+ expect(() => deepSeekPluginInvocation("../profile")).toThrow(
+ "Invalid DeepSeek Harness profile"
+ );
+ for (const profile of [
+ "",
+ ".",
+ "..",
+ "node_modules",
+ "nested/profile",
+ "nested\\profile",
+ "team profile",
+ "team&calc",
+ "team|calc",
+ "teamcalc",
+ "team^calc",
+ "team(calc)",
+ "team%PATH%",
+ "team!calc",
+ "team\ncalc",
+ ]) {
+ expect(() => validateDeepSeekProfile(profile)).toThrow("Invalid DeepSeek Harness profile");
+ }
+ expect(validateDeepSeekProfile("team-profile_1.0")).toBe("team-profile_1.0");
+ });
+
+ test("rejects an empty credential", async () => {
+ await expect(writeDeepSeekCredential("", await temporaryRoot())).rejects.toThrow(
+ "Context7 API key cannot be empty"
+ );
+ expect(CONTEXT7_CREDENTIAL_REF).toBe("CONTEXT7_API_KEY");
+ });
+});
diff --git a/packages/cli/src/commands/setup.ts b/packages/cli/src/commands/setup.ts
index c5b8a3f75..d51641d4d 100644
--- a/packages/cli/src/commands/setup.ts
+++ b/packages/cli/src/commands/setup.ts
@@ -35,6 +35,11 @@ import {
patchStdioApiKey,
getJsonServerEntry,
} from "../setup/mcp-writer.js";
+import {
+ installDeepSeekPlugin,
+ validateDeepSeekProfile,
+ writeDeepSeekCredential,
+} from "../setup/deepseek.js";
type Scope = "global" | "project";
type SetupMode = "mcp" | "cli";
@@ -53,6 +58,7 @@ interface SetupOptions {
cli?: boolean;
mcp?: boolean;
stdio?: boolean;
+ deepseek?: string | boolean;
}
function resolveTransport(options: SetupOptions): Transport {
@@ -87,6 +93,7 @@ export function registerSetupCommand(program: Command): void {
.option("--opencode", "Set up for OpenCode")
.option("--codex", "Set up for Codex")
.option("--gemini", "Set up for Gemini CLI")
+ .option("--deepseek [profile]", "Set up DeepSeek Harness (default profile: headless)")
.option("--mcp", "Set up MCP server mode")
.option("--cli", "Set up CLI + Skills mode (no MCP server)")
.option("-p, --project", "Configure for current project instead of globally")
@@ -142,6 +149,60 @@ async function resolveAuth(options: SetupOptions): Promise {
return { mode: "api-key", apiKey };
}
+async function setupDeepSeek(options: SetupOptions): Promise {
+ if (options.project || options.oauth || options.mcp || options.cli || options.stdio) {
+ log.error("--deepseek cannot be combined with --project, --oauth, --mcp, --cli, or --stdio.");
+ process.exitCode = 1;
+ return;
+ }
+ if (getSelectedAgents(options).length > 0) {
+ log.error("--deepseek cannot be combined with another agent option.");
+ process.exitCode = 1;
+ return;
+ }
+
+ const requestedProfile = typeof options.deepseek === "string" ? options.deepseek : "headless";
+ let profile: string;
+ try {
+ profile = validateDeepSeekProfile(requestedProfile);
+ } catch (error) {
+ log.error(error instanceof Error ? error.message : String(error));
+ process.exitCode = 1;
+ return;
+ }
+
+ const auth = await resolveAuth(options);
+ if (!auth?.apiKey) {
+ log.warn("Setup cancelled");
+ return;
+ }
+ const spinner = ora("Saving Context7 credential...").start();
+ let credentialPath: string;
+ try {
+ credentialPath = await writeDeepSeekCredential(auth.apiKey);
+ spinner.succeed("Saved Context7 credential");
+ } catch (error) {
+ spinner.fail("Failed to save Context7 credential");
+ log.error(error instanceof Error ? error.message : String(error));
+ process.exitCode = 1;
+ return;
+ }
+
+ try {
+ await installDeepSeekPlugin(profile);
+ } catch (error) {
+ log.error(error instanceof Error ? error.message : String(error));
+ process.exitCode = 1;
+ return;
+ }
+
+ log.blank();
+ log.success(`Context7 configured for DeepSeek Harness profile ${pc.bold(profile)}`);
+ log.plain(` Credential ${pc.dim(credentialPath)}`);
+ log.blank();
+ trackEvent("setup", { mode: "deepseek", profile });
+}
+
async function resolveMode(options: SetupOptions): Promise {
if (options.cli) return "cli";
if (options.mcp || options.yes || options.oauth || options.stdio) return "mcp";
@@ -534,6 +595,10 @@ async function setupCommand(options: SetupOptions): Promise {
trackEvent("command", { name: "setup" });
try {
+ if (options.deepseek !== undefined) {
+ await setupDeepSeek(options);
+ return;
+ }
const mode = await resolveMode(options);
if (mode === "mcp") {
const scope: Scope = options.project ? "project" : "global";
diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts
index 2544b9ffb..e45db0c23 100644
--- a/packages/cli/src/index.ts
+++ b/packages/cli/src/index.ts
@@ -43,6 +43,7 @@ Examples:
${brand.primary("npx ctx7 setup")}
${brand.primary("npx ctx7 setup --mcp")}
${brand.primary("npx ctx7 setup --cli")}
+ ${brand.primary("npx ctx7 setup --deepseek headless")}
${brand.dim("# Remove Context7 setup")}
${brand.primary("npx ctx7 remove --cursor")}
diff --git a/packages/cli/src/setup/deepseek.ts b/packages/cli/src/setup/deepseek.ts
new file mode 100644
index 000000000..d5b9ca5a8
--- /dev/null
+++ b/packages/cli/src/setup/deepseek.ts
@@ -0,0 +1,175 @@
+import { spawn } from "node:child_process";
+import { randomBytes } from "node:crypto";
+import { chmod, mkdir, readFile, rename, rm, unlink, writeFile } from "node:fs/promises";
+import { homedir } from "node:os";
+import { join, resolve } from "node:path";
+import { Document, parseDocument } from "yaml";
+
+export const DEEPSEEK_PLUGIN_PACKAGE = "@upstash/context7-deepseek-harness";
+export const CONTEXT7_CREDENTIAL_REF = "CONTEXT7_API_KEY";
+
+const CREDENTIAL_REF_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
+const PROFILE_PATTERN = /^[A-Za-z0-9._-]+$/;
+const LOCK_RETRY_INITIAL_MS = 20;
+const LOCK_RETRY_MAX_MS = 200;
+const LOCK_TIMEOUT_MS = 2_000;
+
+function isEnoent(error: unknown): boolean {
+ return (error as NodeJS.ErrnoException | undefined)?.code === "ENOENT";
+}
+
+function isEexist(error: unknown): boolean {
+ return (error as NodeJS.ErrnoException | undefined)?.code === "EEXIST";
+}
+
+export function resolveDshHome(
+ env: NodeJS.ProcessEnv = process.env,
+ userHome = homedir(),
+ currentDirectory = process.cwd()
+): string {
+ const configured = env.DSH_HOME;
+ const selected =
+ configured !== undefined && configured.trim().length > 0 ? configured : join(userHome, ".dsh");
+ const expanded =
+ selected === "~"
+ ? userHome
+ : selected.startsWith("~/") || selected.startsWith("~\\")
+ ? join(userHome, selected.slice(2))
+ : selected;
+ return resolve(currentDirectory, expanded);
+}
+
+function credentialsDocument(text: string | undefined, filename: string): Document {
+ const document =
+ text === undefined ? new Document({}) : parseDocument(text, { uniqueKeys: true });
+ if (document.errors.length > 0) {
+ throw new Error(`Invalid DeepSeek Harness credentials document at ${filename}`);
+ }
+ const root: unknown = document.toJS() ?? {};
+ if (typeof root !== "object" || root === null || Array.isArray(root)) {
+ throw new Error(`DeepSeek Harness credentials document at ${filename} must be a mapping`);
+ }
+ for (const [ref, value] of Object.entries(root as Record)) {
+ if (!CREDENTIAL_REF_PATTERN.test(ref)) {
+ throw new Error(`Invalid credential reference "${ref}" in ${filename}`);
+ }
+ if (typeof value !== "string" || value.length === 0) {
+ throw new Error(`Credential "${ref}" in ${filename} must have a non-empty string value`);
+ }
+ }
+ return document;
+}
+
+async function withFileLock(filename: string, operation: () => Promise): Promise {
+ const lockPath = `${filename}.lock`;
+ const deadline = Date.now() + LOCK_TIMEOUT_MS;
+ let delay = LOCK_RETRY_INITIAL_MS;
+ for (;;) {
+ try {
+ await writeFile(lockPath, `${process.pid}\n`, { mode: 0o600, flag: "wx" });
+ break;
+ } catch (error) {
+ if (!isEexist(error)) throw error;
+ }
+ if (Date.now() >= deadline) {
+ throw new Error(`Timed out waiting for the credential writer lock at ${lockPath}`);
+ }
+ await new Promise((resolveDelay) => setTimeout(resolveDelay, delay));
+ delay = Math.min(delay * 2, LOCK_RETRY_MAX_MS);
+ }
+ try {
+ return await operation();
+ } finally {
+ await rm(lockPath, { force: true });
+ }
+}
+
+export async function writeDeepSeekCredential(
+ apiKey: string,
+ dshHome = resolveDshHome()
+): Promise {
+ if (!apiKey) throw new Error("Context7 API key cannot be empty");
+ const filename = join(dshHome, ".credentials.yaml");
+ await mkdir(dshHome, { recursive: true, mode: 0o700 });
+ await chmod(dshHome, 0o700);
+ await withFileLock(filename, async () => {
+ let existing: string | undefined;
+ try {
+ existing = await readFile(filename, "utf8");
+ } catch (error) {
+ if (!isEnoent(error)) throw error;
+ }
+ const document = credentialsDocument(existing, filename);
+ document.setIn([CONTEXT7_CREDENTIAL_REF], apiKey);
+ const temporary = join(
+ dshHome,
+ `.credentials.yaml.${process.pid}.${randomBytes(6).toString("hex")}.tmp`
+ );
+ try {
+ await writeFile(temporary, document.toString(), {
+ encoding: "utf8",
+ flag: "wx",
+ mode: 0o600,
+ });
+ await chmod(temporary, 0o600);
+ await rename(temporary, filename);
+ await chmod(filename, 0o600);
+ } finally {
+ await unlink(temporary).catch((error: unknown) => {
+ if (!isEnoent(error)) throw error;
+ });
+ }
+ });
+ return filename;
+}
+
+export function validateDeepSeekProfile(profile: string): string {
+ if (
+ !PROFILE_PATTERN.test(profile) ||
+ profile === "." ||
+ profile === ".." ||
+ profile === "node_modules"
+ ) {
+ throw new Error(`Invalid DeepSeek Harness profile name "${profile}"`);
+ }
+ return profile;
+}
+
+export function deepSeekPluginInvocation(profile: string): {
+ command: string;
+ args: string[];
+} {
+ validateDeepSeekProfile(profile);
+ return {
+ command: "npx",
+ args: [
+ "--yes",
+ "@deepseek-ai/dsh",
+ "plugin",
+ "--profile",
+ profile,
+ "add",
+ DEEPSEEK_PLUGIN_PACKAGE,
+ ],
+ };
+}
+
+export async function installDeepSeekPlugin(profile: string): Promise {
+ const invocation = deepSeekPluginInvocation(profile);
+ const code = await new Promise((resolve, reject) => {
+ const child = spawn(invocation.command, invocation.args, {
+ stdio: "inherit",
+ shell: process.platform === "win32",
+ });
+ child.once("error", reject);
+ child.once("close", resolve);
+ }).catch((error: unknown) => {
+ if (isEnoent(error)) {
+ throw new Error("The `npx` package runner was not found on PATH");
+ }
+ throw error;
+ });
+ if (code !== 0) {
+ throw new Error(`DeepSeek Harness plugin installation failed with exit code ${String(code)}`);
+ }
+}
diff --git a/packages/deepseek-harness/.gitignore b/packages/deepseek-harness/.gitignore
new file mode 100644
index 000000000..2a5015ff1
--- /dev/null
+++ b/packages/deepseek-harness/.gitignore
@@ -0,0 +1,5 @@
+node_modules
+dist
+*.log
+.env
+.env.*
diff --git a/packages/deepseek-harness/LICENSE b/packages/deepseek-harness/LICENSE
new file mode 100644
index 000000000..0a1f72a6b
--- /dev/null
+++ b/packages/deepseek-harness/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2021 Upstash, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/deepseek-harness/README.md b/packages/deepseek-harness/README.md
new file mode 100644
index 000000000..4c96c07dc
--- /dev/null
+++ b/packages/deepseek-harness/README.md
@@ -0,0 +1,64 @@
+# Context7 Plugin for DeepSeek Harness
+
+The official [Context7](https://context7.com) plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness). It adds native `resolve-library-id` and `query-docs` tools so the harness can retrieve current library documentation and code examples. It also adds invocation guidance to the system prompt so library-specific questions use Context7 automatically.
+
+## Installation
+
+Install the plugin and store a Context7 API key in DeepSeek Harness's credential provider:
+
+```bash
+npx ctx7@latest setup --deepseek headless
+```
+
+Pass another profile name instead of `headless` when needed. Without an existing Context7 login, setup opens the device authorization flow and creates an API key. To install the bundle without configuring authentication:
+
+```bash
+dsh plugin --profile headless add @upstash/context7-deepseek-harness
+```
+
+Verify the composed configuration and start the profile:
+
+```bash
+dsh --profile headless --dump-config
+dsh --profile headless
+```
+
+To remove it:
+
+```bash
+dsh plugin --profile headless remove @upstash/context7-deepseek-harness
+```
+
+## Authentication
+
+The plugin works without authentication using Context7's anonymous rate limits. For higher limits, it resolves `CONTEXT7_API_KEY` through DeepSeek Harness's credential provider before every request, so credential updates apply without reloading the plugin.
+
+The setup command stores the key in `$DSH_HOME/.credentials.yaml`, which defaults to `~/.dsh/.credentials.yaml`. You can also provide it through the environment:
+
+```bash
+export CONTEXT7_API_KEY="your-api-key"
+```
+
+For manual credential-file setup, edit the flat YAML mapping and apply owner-only permissions:
+
+```bash
+mkdir -p ~/.dsh
+chmod 700 ~/.dsh
+${EDITOR:-vi} ~/.dsh/.credentials.yaml
+chmod 600 ~/.dsh/.credentials.yaml
+```
+
+```yaml
+CONTEXT7_API_KEY: your-api-key
+```
+
+Do not put API keys in `cordis.yml` or `cordis.patch.yml`; composed configuration can be printed and shared during debugging.
+
+## Tools
+
+- `resolve-library-id` finds Context7-compatible library IDs and available versions.
+- `query-docs` retrieves documentation for a selected library ID and question.
+
+## License
+
+MIT
diff --git a/packages/deepseek-harness/cordis.patch.yml b/packages/deepseek-harness/cordis.patch.yml
new file mode 100644
index 000000000..4729d2044
--- /dev/null
+++ b/packages/deepseek-harness/cordis.patch.yml
@@ -0,0 +1,3 @@
+- insert:
+ - id: context7
+ name: "@upstash/context7-deepseek-harness"
diff --git a/packages/deepseek-harness/eslint.config.js b/packages/deepseek-harness/eslint.config.js
new file mode 100644
index 000000000..e1e72c93a
--- /dev/null
+++ b/packages/deepseek-harness/eslint.config.js
@@ -0,0 +1,37 @@
+import { defineConfig } from "eslint/config";
+import tseslint from "typescript-eslint";
+import eslintPluginPrettier from "eslint-plugin-prettier";
+
+export default defineConfig(
+ {
+ ignores: ["node_modules/**", "dist/**"],
+ },
+ {
+ files: ["**/*.ts"],
+ languageOptions: {
+ ecmaVersion: 2022,
+ sourceType: "module",
+ parser: tseslint.parser,
+ parserOptions: {
+ project: "./tsconfig.json",
+ tsconfigRootDir: import.meta.dirname,
+ },
+ globals: {
+ process: "readonly",
+ fetch: "readonly",
+ Response: "readonly",
+ URL: "readonly",
+ AbortController: "readonly",
+ },
+ },
+ plugins: {
+ "@typescript-eslint": tseslint.plugin,
+ prettier: eslintPluginPrettier,
+ },
+ rules: {
+ ...tseslint.configs.recommended.rules,
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
+ "prettier/prettier": "error",
+ },
+ }
+);
diff --git a/packages/deepseek-harness/package.json b/packages/deepseek-harness/package.json
new file mode 100644
index 000000000..460000ca0
--- /dev/null
+++ b/packages/deepseek-harness/package.json
@@ -0,0 +1,92 @@
+{
+ "name": "@upstash/context7-deepseek-harness",
+ "version": "0.1.0",
+ "description": "Official Context7 plugin for DeepSeek Harness",
+ "type": "module",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.js"
+ }
+ },
+ "files": [
+ "dist",
+ "cordis.patch.yml",
+ "LICENSE",
+ "README.md"
+ ],
+ "dsh": {
+ "bundle": {
+ "patch": "./cordis.patch.yml"
+ }
+ },
+ "scripts": {
+ "build": "tsup",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "typecheck": "tsc --noEmit",
+ "clean": "rm -rf dist",
+ "lint": "eslint .",
+ "lint:check": "eslint .",
+ "format": "prettier --write .",
+ "format:check": "prettier --check ."
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/upstash/context7.git",
+ "directory": "packages/deepseek-harness"
+ },
+ "keywords": [
+ "deepseek",
+ "deepseek-harness",
+ "context7",
+ "documentation",
+ "upstash",
+ "ai",
+ "coding-agent"
+ ],
+ "author": "Upstash",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/upstash/context7/issues"
+ },
+ "homepage": "https://github.com/upstash/context7#readme",
+ "publishConfig": {
+ "access": "public"
+ },
+ "engines": {
+ "node": "^22.19.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@deepseek-ai/cordis": ">=4.0.1 <5",
+ "@deepseek-ai/dsh-credentials": ">=0.1.0-rc.6 <1",
+ "@deepseek-ai/dsh-system-prompt": ">=0.1.0-rc.6 <1",
+ "@deepseek-ai/dsh-tools": ">=0.1.0-rc.6 <1"
+ },
+ "peerDependenciesMeta": {
+ "@deepseek-ai/cordis": {
+ "optional": true
+ },
+ "@deepseek-ai/dsh-credentials": {
+ "optional": true
+ },
+ "@deepseek-ai/dsh-system-prompt": {
+ "optional": true
+ },
+ "@deepseek-ai/dsh-tools": {
+ "optional": true
+ }
+ },
+ "devDependencies": {
+ "@deepseek-ai/cordis": "4.0.1",
+ "@deepseek-ai/dsh-credentials": "0.1.0-rc.6",
+ "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.6",
+ "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
+ "@types/node": "^25.9.1",
+ "tsup": "^8.5.1",
+ "typescript": "^5.9.3",
+ "vitest": "^4.1.9"
+ }
+}
diff --git a/packages/deepseek-harness/src/api.ts b/packages/deepseek-harness/src/api.ts
new file mode 100644
index 000000000..c8a2a47e7
--- /dev/null
+++ b/packages/deepseek-harness/src/api.ts
@@ -0,0 +1,78 @@
+const BASE_URL = "https://context7.com/api/v2";
+
+export interface SearchResult {
+ id: string;
+ title: string;
+ description: string;
+ totalSnippets?: number;
+ trustScore?: number;
+ benchmarkScore?: number;
+ versions?: string[];
+ source?: string;
+}
+
+export interface SearchResponse {
+ error?: string;
+ results: SearchResult[];
+ searchFilterApplied?: boolean;
+}
+
+function headers(apiKey?: string): HeadersInit {
+ return apiKey ? { Authorization: `Bearer ${apiKey}` } : {};
+}
+
+async function errorMessage(response: Response, apiKey?: string): Promise {
+ const body = (await response.json().catch(() => undefined)) as { message?: string } | undefined;
+ if (body?.message) return body.message;
+
+ if (response.status === 429) {
+ return apiKey
+ ? "Rate limited or quota exceeded. Upgrade your plan at https://context7.com/plans for higher limits."
+ : "Rate limited or quota exceeded. Create a free API key at https://context7.com/dashboard for higher limits.";
+ }
+ if (response.status === 404) {
+ return "The requested library does not exist. Resolve the library ID again and choose another result.";
+ }
+ if (response.status === 401) {
+ return "Invalid Context7 API key. API keys should start with the 'ctx7sk' prefix.";
+ }
+ return `Context7 request failed with status ${response.status}.`;
+}
+
+async function request(
+ url: URL,
+ apiKey: string | undefined,
+ signal: AbortSignal
+): Promise {
+ const response = await fetch(url, { headers: headers(apiKey), signal });
+ if (!response.ok) throw new Error(await errorMessage(response, apiKey));
+ return response;
+}
+
+export async function searchLibraries(
+ query: string,
+ libraryName: string,
+ apiKey: string | undefined,
+ signal: AbortSignal
+): Promise {
+ const url = new URL(`${BASE_URL}/libs/search`);
+ url.searchParams.set("query", query);
+ url.searchParams.set("libraryName", libraryName);
+ const response = await request(url, apiKey, signal);
+ return (await response.json()) as SearchResponse;
+}
+
+export async function fetchLibraryContext(
+ query: string,
+ libraryId: string,
+ apiKey: string | undefined,
+ signal: AbortSignal
+): Promise {
+ const url = new URL(`${BASE_URL}/context`);
+ url.searchParams.set("query", query);
+ url.searchParams.set("libraryId", libraryId);
+ const response = await request(url, apiKey, signal);
+ const text = await response.text();
+ if (text) return text;
+ return "Documentation not found for this library. Resolve the library ID again and choose another result.";
+}
diff --git a/packages/deepseek-harness/src/format.ts b/packages/deepseek-harness/src/format.ts
new file mode 100644
index 000000000..64319485b
--- /dev/null
+++ b/packages/deepseek-harness/src/format.ts
@@ -0,0 +1,42 @@
+import type { SearchResponse, SearchResult } from "./api.js";
+
+function reputation(score?: number): "High" | "Medium" | "Low" | "Unknown" {
+ if (score === undefined || score < 0) return "Unknown";
+ if (score >= 7) return "High";
+ if (score >= 4) return "Medium";
+ return "Low";
+}
+
+function formatResult(result: SearchResult): string {
+ const lines = [
+ `- Title: ${result.title}`,
+ `- Context7-compatible library ID: ${result.id}`,
+ `- Description: ${result.description}`,
+ ];
+ if (result.totalSnippets !== undefined && result.totalSnippets !== -1) {
+ lines.push(`- Code Snippets: ${result.totalSnippets}`);
+ }
+ lines.push(`- Source Reputation: ${reputation(result.trustScore)}`);
+ if (result.benchmarkScore !== undefined && result.benchmarkScore > 0) {
+ lines.push(`- Benchmark Score: ${result.benchmarkScore}`);
+ }
+ if (result.versions?.length) {
+ lines.push(`- Versions: ${result.versions.join(", ")}`);
+ }
+ if (result.source) {
+ lines.push(`- Source: ${result.source}`);
+ }
+ return lines.join("\n");
+}
+
+export function formatSearchResults(response: SearchResponse): string {
+ if (response.results.length === 0) return "No libraries found matching the provided name.";
+ const parts = [];
+ if (response.searchFilterApplied) {
+ parts.push(
+ "**Note:** Your results only include libraries matching your teamspace's library filters. To adjust quality thresholds or blocked libraries, update your filters at https://context7.com/dashboard?tab=policies"
+ );
+ }
+ parts.push(response.results.map(formatResult).join("\n----------\n"));
+ return `Available Libraries:\n\n${parts.join("\n\n")}`;
+}
diff --git a/packages/deepseek-harness/src/index.ts b/packages/deepseek-harness/src/index.ts
new file mode 100644
index 000000000..812b679b7
--- /dev/null
+++ b/packages/deepseek-harness/src/index.ts
@@ -0,0 +1,104 @@
+import type { Context } from "@deepseek-ai/cordis";
+import { credentialRef } from "@deepseek-ai/dsh-credentials";
+import type { PromptSection } from "@deepseek-ai/dsh-system-prompt";
+import { defineTool } from "@deepseek-ai/dsh-tools";
+import { fetchLibraryContext, searchLibraries } from "./api.js";
+import { formatSearchResults } from "./format.js";
+
+export const name = "context7";
+export const inject = ["credentials", "tools", "systemPrompt"];
+
+const RESOLVE_DESCRIPTION = `Resolves a package or product name to a Context7-compatible library ID and returns matching libraries.
+
+Call this tool before query-docs unless the user explicitly provides a library ID in /org/project or /org/project/version format. Select the closest official match using name, source reputation, snippet coverage, benchmark score, and version.`;
+
+const QUERY_DESCRIPTION = `Retrieves current documentation and code examples from Context7 for a library.
+
+Call resolve-library-id first unless the user explicitly provides a library ID in /org/project or /org/project/version format. Use a specific query scoped to one concept and do not include secrets, credentials, personal data, or proprietary code.`;
+
+const API_TIMEOUT_MS = 60_000;
+const API_KEY_REF = credentialRef("CONTEXT7_API_KEY");
+
+const CONTEXT7_PROMPT: PromptSection = {
+ name: "context7:tool-guidance",
+ order: 120,
+ text: `Use Context7 to fetch current documentation whenever the user asks about a library, framework, SDK, API, CLI tool, or cloud service, even well-known ones. This includes API syntax, configuration, version migration, library-specific debugging, setup instructions, and CLI tool usage. Use it even when you think you know the answer because training data may not reflect recent changes. Prefer Context7 over web search for library documentation.
+
+Do not use Context7 for refactoring, writing scripts from scratch, debugging business logic, code review, repository-local code, or general programming concepts.
+
+Workflow:
+1. Call resolve-library-id with the official library name and the user's specific goal unless the user provides a Context7 library ID in /org/project or /org/project/version format.
+2. Select the best match using exact name match, description relevance, snippet coverage, source reputation, benchmark score, and requested version. Prefer official sources.
+3. Call query-docs with the selected library ID and a specific query scoped to one concept. Split distinct concepts into separate calls with the same library ID unless the question is about how they interact.
+4. Answer using the fetched documentation.
+
+Do not call either tool more than three times per question. Never include secrets, credentials, personal data, or proprietary code in a query.`,
+};
+
+export function apply(ctx: Context): void {
+ ctx.systemPrompt.section(CONTEXT7_PROMPT);
+
+ ctx.tools.register(
+ defineTool({
+ name: "resolve-library-id",
+ description: RESOLVE_DESCRIPTION,
+ parameters: {
+ query: {
+ type: "string",
+ required: true,
+ description:
+ "What to look up in the library documentation. Include the user's goal so Context7 can rank results by relevance.",
+ },
+ libraryName: {
+ type: "string",
+ required: true,
+ description:
+ "Official library or product name with its normal spelling and punctuation, such as Next.js, Customer.io, or Three.js.",
+ },
+ },
+ output: {
+ schema: { type: "string" },
+ render: (_args, value) => [{ type: "text", text: value }],
+ },
+ timeoutMs: API_TIMEOUT_MS,
+ isConcurrencySafe: () => true,
+ async execute(args, exec) {
+ const apiKey = (await ctx.credentials.resolve(API_KEY_REF))?.value;
+ const response = await searchLibraries(args.query, args.libraryName, apiKey, exec.signal);
+ if (response.results.length === 0 && response.error) throw new Error(response.error);
+ return formatSearchResults(response);
+ },
+ })
+ );
+
+ ctx.tools.register(
+ defineTool({
+ name: "query-docs",
+ description: QUERY_DESCRIPTION,
+ parameters: {
+ libraryId: {
+ type: "string",
+ required: true,
+ description:
+ "Exact Context7-compatible library ID returned by resolve-library-id, such as /vercel/next.js or /vercel/next.js/v15.1.8.",
+ },
+ query: {
+ type: "string",
+ required: true,
+ description:
+ "Specific documentation question scoped to one concept. Include relevant API names and versions.",
+ },
+ },
+ output: {
+ schema: { type: "string" },
+ render: (_args, value) => [{ type: "text", text: value }],
+ },
+ timeoutMs: API_TIMEOUT_MS,
+ isConcurrencySafe: () => true,
+ async execute(args, exec) {
+ const apiKey = (await ctx.credentials.resolve(API_KEY_REF))?.value;
+ return fetchLibraryContext(args.query, args.libraryId, apiKey, exec.signal);
+ },
+ })
+ );
+}
diff --git a/packages/deepseek-harness/test/plugin.test.ts b/packages/deepseek-harness/test/plugin.test.ts
new file mode 100644
index 000000000..feece805c
--- /dev/null
+++ b/packages/deepseek-harness/test/plugin.test.ts
@@ -0,0 +1,296 @@
+import { readFile } from "node:fs/promises";
+import { Context } from "@deepseek-ai/cordis";
+import CredentialProvider, {
+ credentialRef,
+ type CredentialInfo,
+ type CredentialRef,
+ type ResolvedCredential,
+} from "@deepseek-ai/dsh-credentials";
+import SystemPrompt, { renderPrompt, type PromptSection } from "@deepseek-ai/dsh-system-prompt";
+import ToolRuntime, { type ToolDefinition, type ToolExecutionInput } from "@deepseek-ai/dsh-tools";
+import { afterEach, describe, expect, it, vi } from "vitest";
+import * as context7 from "../src/index.js";
+
+const { apply } = context7;
+const API_KEY_REF = credentialRef("CONTEXT7_API_KEY");
+
+class MemoryCredentials extends CredentialProvider {
+ private apiKey?: string;
+
+ constructor(ctx: Context, config: { apiKey?: string } = {}) {
+ super(ctx);
+ this.apiKey = config.apiKey;
+ }
+
+ resolve(ref: CredentialRef): Promise {
+ return Promise.resolve(
+ ref === API_KEY_REF && this.apiKey ? { value: this.apiKey, source: "memory" } : undefined
+ );
+ }
+
+ describe(ref: CredentialRef): Promise {
+ return Promise.resolve({
+ configured: ref === API_KEY_REF && Boolean(this.apiKey),
+ writable: true,
+ });
+ }
+
+ set(ref: CredentialRef, value: string): Promise {
+ if (ref === API_KEY_REF) this.apiKey = value;
+ return Promise.resolve();
+ }
+
+ unset(ref: CredentialRef): Promise {
+ if (ref === API_KEY_REF) this.apiKey = undefined;
+ return Promise.resolve();
+ }
+}
+
+function loadTools(apiKey?: string): Map {
+ const tools = new Map();
+ const ctx = {
+ credentials: {
+ resolve: () => Promise.resolve(apiKey ? { value: apiKey, source: "memory" } : undefined),
+ },
+ tools: {
+ register(tool: ToolDefinition) {
+ tools.set(tool.name, tool);
+ },
+ },
+ systemPrompt: {
+ section(_section: PromptSection) {
+ return () => undefined;
+ },
+ },
+ } as unknown as Context;
+ apply(ctx);
+ return tools;
+}
+
+async function loadRuntime(apiKey?: string): Promise {
+ const root = new Context();
+ await root.plugin(MemoryCredentials, { apiKey });
+ await root.plugin(SystemPrompt, {
+ includeHarnessIdentity: false,
+ includeRuntimeContext: false,
+ persona: "",
+ });
+ await root.plugin(ToolRuntime);
+ await root.plugin(context7);
+ return root;
+}
+
+function execution() {
+ return { signal: new AbortController().signal } as never;
+}
+
+function toolInput(
+ name: string,
+ args: Record,
+ signal = new AbortController().signal
+): ToolExecutionInput {
+ return {
+ callId: name as never,
+ name,
+ arguments: args,
+ signal,
+ };
+}
+
+afterEach(() => {
+ vi.unstubAllGlobals();
+ vi.unstubAllEnvs();
+});
+
+describe("Context7 DeepSeek Harness plugin", () => {
+ it("registers both Context7 tools", () => {
+ const tools = loadTools();
+ expect([...tools.keys()]).toEqual(["resolve-library-id", "query-docs"]);
+ expect([...tools.values()].map(({ timeoutMs }) => timeoutMs)).toEqual([60_000, 60_000]);
+ });
+
+ it("activates the bundle in the real Cordis tool runtime", async () => {
+ vi.stubGlobal(
+ "fetch",
+ vi.fn((input: URL) =>
+ Promise.resolve(
+ input.pathname.endsWith("/libs/search")
+ ? new Response(
+ JSON.stringify({
+ results: [
+ {
+ id: "/vercel/next.js",
+ title: "Next.js",
+ description: "The React framework",
+ },
+ ],
+ })
+ )
+ : new Response("Current documentation")
+ )
+ )
+ );
+
+ const manifest = JSON.parse(
+ await readFile(new URL("../package.json", import.meta.url), "utf8")
+ ) as { dsh: { bundle: { patch: string } } };
+ const patch = await readFile(
+ new URL(`../${manifest.dsh.bundle.patch}`, import.meta.url),
+ "utf8"
+ );
+ expect(patch).toBe(
+ '- insert:\n - id: context7\n name: "@upstash/context7-deepseek-harness"\n'
+ );
+
+ const root = await loadRuntime();
+
+ const toolNames = ["resolve-library-id", "query-docs"];
+ expect(root.tools.schemas().map(({ name }) => name)).toEqual(toolNames);
+ const assembly = await root.systemPrompt.assemble();
+ expect(assembly.tools.map(({ name }) => name).sort()).toEqual([...toolNames].sort());
+ const prompt = renderPrompt(assembly);
+ expect(prompt).toContain(
+ "Use Context7 to fetch current documentation whenever the user asks about a library"
+ );
+ expect(prompt).toContain(
+ "Do not use Context7 for refactoring, writing scripts from scratch, debugging business logic"
+ );
+ expect(prompt).toContain(
+ "Call resolve-library-id with the official library name and the user's specific goal"
+ );
+ expect(context7.inject).toEqual(["credentials", "tools", "systemPrompt"]);
+ const resolveInput = toolInput("resolve-library-id", {
+ query: "middleware",
+ libraryName: "Next.js",
+ });
+ const queryInput = toolInput("query-docs", {
+ libraryId: "/vercel/next.js",
+ query: "middleware",
+ });
+ expect(root.tools.executionMode(resolveInput)).toEqual({ kind: "parallel" });
+ expect(root.tools.executionMode(queryInput)).toEqual({ kind: "parallel" });
+
+ const results = await Promise.all([
+ root.tools.execute(resolveInput),
+ root.tools.execute(queryInput),
+ ]);
+ expect(results.map(({ isError }) => isError)).toEqual([false, false]);
+
+ await root.fiber.dispose();
+ });
+
+ it("propagates cancellation to Context7 requests", async () => {
+ let notifyFetchStarted: () => void = () => undefined;
+ let observedSignal: AbortSignal | undefined;
+ const fetchStarted = new Promise((resolve) => {
+ notifyFetchStarted = resolve;
+ });
+ vi.stubGlobal(
+ "fetch",
+ vi.fn((_input: URL, init?: RequestInit) => {
+ const signal = init?.signal;
+ if (!signal) throw new Error("Missing request signal");
+ observedSignal = signal;
+ notifyFetchStarted();
+ return new Promise((_resolve, reject) => {
+ const abort = () => reject(signal.reason);
+ if (signal.aborted) abort();
+ else signal.addEventListener("abort", abort, { once: true });
+ });
+ })
+ );
+
+ const root = await loadRuntime();
+ const controller = new AbortController();
+ const pending = root.tools.execute(
+ toolInput(
+ "query-docs",
+ { libraryId: "/vercel/next.js", query: "middleware" },
+ controller.signal
+ )
+ );
+ await fetchStarted;
+ controller.abort(new Error("cancelled"));
+
+ await expect(pending).resolves.toMatchObject({ isError: true });
+ expect(observedSignal?.aborted).toBe(true);
+
+ await root.fiber.dispose();
+ });
+
+ it("resolves libraries with authentication and formats the response", async () => {
+ const fetchMock = vi.fn().mockResolvedValue(
+ new Response(
+ JSON.stringify({
+ searchFilterApplied: true,
+ results: [
+ {
+ id: "/vercel/next.js",
+ title: "Next.js",
+ description: "The React framework",
+ totalSnippets: 100,
+ trustScore: 10,
+ benchmarkScore: 92,
+ versions: ["v15.1.8"],
+ source: "https://nextjs.org/docs",
+ },
+ ],
+ })
+ )
+ );
+ vi.stubGlobal("fetch", fetchMock);
+ const tool = loadTools("ctx7sk-test").get("resolve-library-id")!;
+ const result = await tool.execute({ query: "middleware", libraryName: "Next.js" }, execution());
+
+ expect(result).toContain("/vercel/next.js");
+ expect(result).toContain("teamspace's library filters");
+ expect(result).toContain("Source: https://nextjs.org/docs");
+ expect(fetchMock).toHaveBeenCalledWith(
+ expect.objectContaining({
+ href: expect.stringContaining("libraryName=Next.js"),
+ }),
+ expect.objectContaining({
+ headers: { Authorization: "Bearer ctx7sk-test" },
+ })
+ );
+ });
+
+ it("resolves the Context7 credential for every request", async () => {
+ const fetchMock = vi.fn().mockResolvedValue(new Response("Current documentation"));
+ vi.stubGlobal("fetch", fetchMock);
+ const root = await loadRuntime("ctx7sk-first");
+ const input = toolInput("query-docs", {
+ libraryId: "/vercel/next.js",
+ query: "middleware",
+ });
+
+ await root.tools.execute(input);
+ await root.credentials.set(API_KEY_REF, "ctx7sk-second");
+ await root.tools.execute(input);
+
+ expect(fetchMock.mock.calls.map(([, init]) => init.headers.Authorization)).toEqual([
+ "Bearer ctx7sk-first",
+ "Bearer ctx7sk-second",
+ ]);
+
+ await root.fiber.dispose();
+ });
+
+ it("queries documentation without requiring an API key", async () => {
+ const fetchMock = vi.fn().mockResolvedValue(new Response("Current documentation"));
+ vi.stubGlobal("fetch", fetchMock);
+ const tool = loadTools().get("query-docs")!;
+ const result = await tool.execute(
+ { libraryId: "/vercel/next.js", query: "middleware" },
+ execution()
+ );
+
+ expect(result).toBe("Current documentation");
+ expect(fetchMock).toHaveBeenCalledWith(
+ expect.objectContaining({
+ href: expect.stringContaining("libraryId=%2Fvercel%2Fnext.js"),
+ }),
+ expect.objectContaining({ headers: {} })
+ );
+ });
+});
diff --git a/packages/deepseek-harness/tsconfig.json b/packages/deepseek-harness/tsconfig.json
new file mode 100644
index 000000000..85319127e
--- /dev/null
+++ b/packages/deepseek-harness/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "lib": ["ES2023", "DOM"],
+ "module": "ESNext",
+ "target": "ES2022",
+ "moduleResolution": "bundler",
+ "moduleDetection": "force",
+ "noEmit": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true,
+ "types": ["node", "vitest/globals"]
+ },
+ "include": ["src/**/*", "test/**/*", "tsup.config.ts", "vitest.config.ts", "eslint.config.js"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/packages/deepseek-harness/tsup.config.ts b/packages/deepseek-harness/tsup.config.ts
new file mode 100644
index 000000000..82a5a34fc
--- /dev/null
+++ b/packages/deepseek-harness/tsup.config.ts
@@ -0,0 +1,16 @@
+import { defineConfig } from "tsup";
+
+export default defineConfig({
+ entry: ["src/index.ts"],
+ format: ["esm"],
+ dts: true,
+ clean: true,
+ sourcemap: true,
+ target: "node22",
+ external: [
+ "@deepseek-ai/cordis",
+ "@deepseek-ai/dsh-credentials",
+ "@deepseek-ai/dsh-system-prompt",
+ "@deepseek-ai/dsh-tools",
+ ],
+});
diff --git a/packages/deepseek-harness/vitest.config.ts b/packages/deepseek-harness/vitest.config.ts
new file mode 100644
index 000000000..ed8bf7739
--- /dev/null
+++ b/packages/deepseek-harness/vitest.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from "vitest/config";
+
+export default defineConfig({
+ test: {
+ environment: "node",
+ include: ["test/**/*.test.ts"],
+ },
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e3161a1d5..91ce31c09 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -72,6 +72,9 @@ importers:
picocolors:
specifier: ^1.1.1
version: 1.1.1
+ yaml:
+ specifier: ^2.9.0
+ version: 2.9.0
devDependencies:
'@types/figlet':
specifier: ^1.7.0
@@ -107,6 +110,33 @@ importers:
specifier: ^4.1.9
version: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@22.19.7)(vite@7.2.4(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0))
+ packages/deepseek-harness:
+ devDependencies:
+ '@deepseek-ai/cordis':
+ specifier: 4.0.1
+ version: 4.0.1
+ '@deepseek-ai/dsh-credentials':
+ specifier: 0.1.0-rc.6
+ version: 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-system-prompt':
+ specifier: 0.1.0-rc.6
+ version: 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-llm@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))))(@deepseek-ai/dsh-scope@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))
+ '@deepseek-ai/dsh-tools':
+ specifier: 0.1.0-rc.6
+ version: 0.1.0-rc.6(f8724372086ccc1457fc84e7becee2e0)
+ '@types/node':
+ specifier: ^25.9.1
+ version: 25.9.1
+ tsup:
+ specifier: ^8.5.1
+ version: 8.5.1(jiti@2.7.0)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0)
+ typescript:
+ specifier: ^5.9.3
+ version: 5.9.3
+ vitest:
+ specifier: ^4.1.9
+ version: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@25.9.1)(vite@7.2.4(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0))
+
packages/mcp:
dependencies:
'@modelcontextprotocol/node':
@@ -419,6 +449,136 @@ packages:
'@changesets/write@0.4.0':
resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==}
+ '@deepseek-ai/cordis@4.0.1':
+ resolution: {integrity: sha512-YBdskTU2Po1kru3GgcUWUbkTsPMA9LkSQDAY8rBkFJeajdgcQad3QPJZE26JyK99Xb6HaASvoXg2DSUTeN/0Nw==}
+ hasBin: true
+ peerDependencies:
+ '@deepseek-ai/cordis-plugin-include': ^1.0.6
+ '@deepseek-ai/cordis-plugin-loader': ^1.0.2
+ peerDependenciesMeta:
+ '@deepseek-ai/cordis-plugin-include':
+ optional: true
+ '@deepseek-ai/cordis-plugin-loader':
+ optional: true
+
+ '@deepseek-ai/cosmokit@1.8.2':
+ resolution: {integrity: sha512-muBOKtSrUKU5m/xpq8ZXWL6hQ/jgd4PhU2PqH97bcxIiLEJfNwZOGQEx4t/aS/GgxRAR+ra9pMHPMtTHU4sqqA==}
+
+ '@deepseek-ai/dsh-agent@0.1.0-rc.6':
+ resolution: {integrity: sha512-vtqq2pWTrzn0dKfj5kREZRpP82AwtGjGx9V1lYnKvF+Uc/a8zyWbSvjDE7V1d3YQAQJzs2cWO31hURWDekDXIA==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-llm': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-scope': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-session': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-system-prompt': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-typert-protocol': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-attachment@0.1.0-rc.6':
+ resolution: {integrity: sha512-3P6N17NQ8jqSQGzeCs+svCIqArU8oq0YmgEAo+axN9aVuUDferWU4DLRSX59UGpmyldX4LQn81toA+c+DqMcHg==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-brand': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-brand@0.1.0-rc.6':
+ resolution: {integrity: sha512-E8j9Nby24qP4rfrdcfc7bpt1CHpGT3tYmycOJJkEOH4ptIdT1m2ro9nmnSd5CWYukTr64A77vjm2WGqHRI92UA==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-code-runtime@0.1.0-rc.6':
+ resolution: {integrity: sha512-aw8D4IOeMo11A3uxQeE4LFoW3bvaQnVkGFqqtS+lsINDARrOCJHXLUgecoKpys+Lc5erZZ4UQDnymJmL4OCcKA==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-credentials@0.1.0-rc.6':
+ resolution: {integrity: sha512-zyYRs3A9gxfZjZfONzJdMhM0Gzbslpha+FGYkLDRAozgPj0N7mZdE+Qflx2V4WNrCnsSjuvOW0HFBxWtSRUTUw==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-brand': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-invariants@0.1.0-rc.6':
+ resolution: {integrity: sha512-WfEfOi99a4cpOugRAHTBSTnesLieu3ist1q9PXDXFBHX++K1rAl9+sB7YrdnbB8LH0UOY532gS9xJUYU6w0SLw==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+
+ '@deepseek-ai/dsh-llm@0.1.0-rc.6':
+ resolution: {integrity: sha512-kuFGC8bHlzGTwlRxQhXjf3CYWl8M4NzH+EYIkrW8rri4iMc9W53xrdvkil5No/DUlMm8g1u7GdeiWYFy0TMvtA==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-attachment': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-brand': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-timeout': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-scope@0.1.0-rc.6':
+ resolution: {integrity: sha512-UlDLV4syLoJinNg9imhXrSAHrdaTa5Ff8gg46rzjFJGPUOhAk3DZff0hryT5OhrBi0A5Tj92qVpg2pRVvxnUzQ==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-session@0.1.0-rc.6':
+ resolution: {integrity: sha512-8tu8I6VWC7050GAUXWhcEWQw4pakALQc8TlhKr52m7Y4+kIKeNt3FBgP86PaGPBtpK0p5zUPRQNkFpzZbBdxyw==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-brand': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-llm': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-scope': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-typert-protocol': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-system-prompt@0.1.0-rc.6':
+ resolution: {integrity: sha512-E7g+XChh4q4/wX++v56z1pV4SA1Rtz42xkznLPPi9FlXrrzJxwHMOUzBZ9Rz3Y1kLhQ++HJG3ZatmNx3rjFilg==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-llm': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-scope': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-timeout@0.1.0-rc.6':
+ resolution: {integrity: sha512-CUean0fAnfsJVszFEip7PsU/S26W+JfDFfsza2dCtlw8n6xlkbHA9Gjxdk2aTwqDGCgXEPkRW7mYkdJ0n6FR7w==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-tools@0.1.0-rc.6':
+ resolution: {integrity: sha512-Tu08EPK3JyK0iNjH4FGzu/1uADynNSS6SmwOLdfytUN0YNqwNuKFSt2OJUg19famNlTgy992DcHfDu0T+gLXFg==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-agent': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-code-runtime': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-llm': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-scope': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-session': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-system-prompt': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-user-approval': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-typert-protocol@0.1.0-rc.6':
+ resolution: {integrity: sha512-weWzN8r01YCkoDCAM7BsKw2YhRrD4zL8N2SAZu9hovYtXSq8xHXsP4Zh8RLYIlYcuotjyff/6hic+0TJPd14YA==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+
+ '@deepseek-ai/dsh-user-approval@0.1.0-rc.6':
+ resolution: {integrity: sha512-9rnkSDGOpu2XUeGwbPeTzVUTFWTND1PMPM5L/ZQPptV5yyZlQiNxM2rCC6OdL+ZVerwxEqrRhZIQn/KVtQfKag==}
+ peerDependencies:
+ '@deepseek-ai/cordis': ^4.0.1
+ '@deepseek-ai/dsh-agent': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-brand': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-invariants': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-llm': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-scope': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-session': ^0.1.0-rc.6
+ '@deepseek-ai/dsh-system-prompt': ^0.1.0-rc.6
+
+ '@deepseek-ai/schemastery@3.18.1':
+ resolution: {integrity: sha512-Qn0FCSwCQnpnj6SB31I6i2sIKgKWnkbJM8O0EU91Gv2UsYVvtZTl6IA0sCwk2e2MZf5S8w5hpq9QkeVvK9qwxg==}
+
'@earendil-works/pi-agent-core@0.78.0':
resolution: {integrity: sha512-xhWd59Qzd8yO88gYQw2S4dEQstJJEiUtxRP01//YzVJ61jCtUASMfcyAmYhgGYR4Onp7GmwEAbBBGOiV6Iwk9g==}
engines: {node: '>=22.19.0'}
@@ -3557,6 +3717,121 @@ snapshots:
human-id: 4.1.3
prettier: 2.8.8
+ '@deepseek-ai/cordis@4.0.1':
+ dependencies:
+ '@deepseek-ai/cosmokit': 1.8.2
+ '@standard-schema/spec': 1.1.0
+
+ '@deepseek-ai/cosmokit@1.8.2': {}
+
+ '@deepseek-ai/dsh-agent@0.1.0-rc.6(b0514d7a320728b6d8f5a31eb3960e10)':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+ '@deepseek-ai/dsh-llm': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))
+ '@deepseek-ai/dsh-scope': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-session': 0.1.0-rc.6(6fd26f59436a18b115f326d6060415e6)
+ '@deepseek-ai/dsh-system-prompt': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-llm@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))))(@deepseek-ai/dsh-scope@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))
+ '@deepseek-ai/dsh-typert-protocol': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+
+ '@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-brand': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+
+ '@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+
+ '@deepseek-ai/dsh-code-runtime@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+
+ '@deepseek-ai/dsh-credentials@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-brand': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+
+ '@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/schemastery': 3.18.1
+
+ '@deepseek-ai/dsh-llm@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-attachment': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-brand': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+ '@deepseek-ai/dsh-timeout': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/schemastery': 3.18.1
+
+ '@deepseek-ai/dsh-scope@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+
+ '@deepseek-ai/dsh-session@0.1.0-rc.6(6fd26f59436a18b115f326d6060415e6)':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-brand': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+ '@deepseek-ai/dsh-llm': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))
+ '@deepseek-ai/dsh-scope': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-typert-protocol': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+
+ '@deepseek-ai/dsh-system-prompt@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-llm@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))))(@deepseek-ai/dsh-scope@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+ '@deepseek-ai/dsh-llm': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))
+ '@deepseek-ai/dsh-scope': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/schemastery': 3.18.1
+
+ '@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+
+ '@deepseek-ai/dsh-tools@0.1.0-rc.6(f8724372086ccc1457fc84e7becee2e0)':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-agent': 0.1.0-rc.6(b0514d7a320728b6d8f5a31eb3960e10)
+ '@deepseek-ai/dsh-code-runtime': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+ '@deepseek-ai/dsh-llm': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))
+ '@deepseek-ai/dsh-scope': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-session': 0.1.0-rc.6(6fd26f59436a18b115f326d6060415e6)
+ '@deepseek-ai/dsh-system-prompt': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-llm@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))))(@deepseek-ai/dsh-scope@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))
+ '@deepseek-ai/dsh-user-approval': 0.1.0-rc.6(dbdaba06c174c5e30e3a58edf3cc27a9)
+ '@deepseek-ai/schemastery': 3.18.1
+
+ '@deepseek-ai/dsh-typert-protocol@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+
+ '@deepseek-ai/dsh-user-approval@0.1.0-rc.6(dbdaba06c174c5e30e3a58edf3cc27a9)':
+ dependencies:
+ '@deepseek-ai/cordis': 4.0.1
+ '@deepseek-ai/dsh-agent': 0.1.0-rc.6(b0514d7a320728b6d8f5a31eb3960e10)
+ '@deepseek-ai/dsh-brand': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-invariants': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)
+ '@deepseek-ai/dsh-llm': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))
+ '@deepseek-ai/dsh-scope': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))
+ '@deepseek-ai/dsh-session': 0.1.0-rc.6(6fd26f59436a18b115f326d6060415e6)
+ '@deepseek-ai/dsh-system-prompt': 0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-llm@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-attachment@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-brand@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))(@deepseek-ai/dsh-timeout@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1))))(@deepseek-ai/dsh-scope@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)(@deepseek-ai/dsh-invariants@0.1.0-rc.6(@deepseek-ai/cordis@4.0.1)))
+ '@deepseek-ai/schemastery': 3.18.1
+
+ '@deepseek-ai/schemastery@3.18.1':
+ dependencies:
+ '@deepseek-ai/cosmokit': 1.8.2
+ '@standard-schema/spec': 1.1.0
+
'@earendil-works/pi-agent-core@0.78.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.20.1)(zod@4.4.3)':
dependencies:
'@earendil-works/pi-ai': 0.78.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.20.1)(zod@4.4.3)