Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ jobs:
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --filter @parallel-web/dsh-web-search... --frozen-lockfile
run: pnpm install --filter @parallel-web/dsh-web-search... --filter @parallel-web/dsh-responses-subagent... --frozen-lockfile

- name: Run deterministic plugin checks
run: pnpm --filter @parallel-web/dsh-web-search check
run: pnpm --filter @parallel-web/dsh-web-search --filter @parallel-web/dsh-responses-subagent run check

- name: Verify packed DSH profile lifecycle
shell: bash
Expand All @@ -133,6 +133,47 @@ jobs:
node packages/dsh-web-search/scripts/verify-packed-profile.mjs \
--dsh-home "$dsh_home" \
--expected-version "$version"

pnpm --dir packages/dsh-responses-subagent pack --pack-destination "$pack_dir"
responses_artifact="$(find "$pack_dir" -name '*dsh-responses-subagent*.tgz' -print -quit)"
tar -xOzf "$responses_artifact" package/package.json | node -e '
const manifest = JSON.parse(require("node:fs").readFileSync(0, "utf8"));
if (manifest.private || manifest.publishConfig?.access !== "public" ||
manifest.dsh?.bundle?.patch !== "./cordis.patch.yml") {
throw new Error("invalid packed Responses plugin manifest");
}
'
DSH_HOME="$dsh_home" pnpm --dir packages/dsh-web-search exec dsh \
plugin --profile web add "$responses_artifact"
DSH_HOME="$dsh_home" pnpm --dir packages/dsh-web-search exec dsh \
--profile web --dump-config > "$dsh_home/combined.yml"
grep -Fq 'id: subagent-parallel-responses' "$dsh_home/combined.yml"
grep -Fq 'toolName: parallel_research' "$dsh_home/combined.yml"
grep -Fq 'id: web-search-parallel' "$dsh_home/combined.yml"

node --input-type=module -e '
import assert from "node:assert/strict";
import { createRequire } from "node:module";
import { join } from "node:path";

const require = createRequire(join(process.argv[1], "profiles/web/package.json"));
const plugin = await import(require.resolve("@parallel-web/dsh-responses-subagent"));
const { Context } = await import(require.resolve("@deepseek-ai/cordis"));
const subagents = await import(require.resolve("@deepseek-ai/dsh-subagent"));
const context = new Context();
context.provide("systemPrompt", { section() {} });
await context.plugin(subagents.default);
await context.plugin(plugin, { apiKey: "parallel_test_packed_profile" });
assert.equal(plugin.Config.dict.apiKey.meta.role, "secret");
assert.equal(context.subagents.getProvider("parallel-responses")?.inheritsParentContext, false);
' "$dsh_home"

DSH_HOME="$dsh_home" pnpm --dir packages/dsh-web-search exec dsh \
plugin --profile web remove @parallel-web/dsh-responses-subagent
DSH_HOME="$dsh_home" pnpm --dir packages/dsh-web-search exec dsh \
--profile web --dump-config > "$dsh_home/responses-removed.yml"
cmp "$dsh_home/after.yml" "$dsh_home/responses-removed.yml"

DSH_HOME="$dsh_home" pnpm --dir packages/dsh-web-search exec dsh \
plugin --profile web remove @parallel-web/dsh-web-search
DSH_HOME="$dsh_home" pnpm --dir packages/dsh-web-search exec dsh \
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ node_modules/
# Build output
dist/
packages/dsh-web-search/lib/
packages/dsh-responses-subagent/lib/
*.tsbuildinfo

# Testing
Expand Down
13 changes: 9 additions & 4 deletions PUBLISHING.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Publishing Guide

This monorepo publishes four npm packages, each versioned, tagged, and released **independently**:
This monorepo tracks five publishable npm packages, each versioned, tagged, and released
**independently**:

- `@parallel-web/ai-sdk-tools` — `packages/ai-sdk-tools`
- `@parallel-web/dsh-responses-subagent` — `packages/dsh-responses-subagent`
- `@parallel-web/dsh-web-search` — `packages/dsh-web-search`
- `@parallel-web/opencode-plugin` — `packages/opencode-plugin`
- `@parallel-web/pi-extension` — `packages/pi-extension`

`@parallel-web/dsh-responses-subagent` has not yet been published. It can be installed only
from a local tarball until an npm organization owner completes its reviewed first release.

(`@parallel-web/oauth` in `packages/parallel-oauth` is `private` — it is bundled into the
OpenCode plugin and Pi extension at build time and is never published.)

Expand Down Expand Up @@ -57,12 +62,12 @@ the reviewed bootstrap release manually from a clean, updated `main` checkout:

```bash
pnpm install --frozen-lockfile
pnpm --filter @parallel-web/dsh-web-search check
pnpm --filter @parallel-web/dsh-responses-subagent check
BOOTSTRAP_DIR="$(mktemp -d)"
pnpm --dir packages/dsh-web-search pack --pack-destination "$BOOTSTRAP_DIR"
pnpm --dir packages/dsh-responses-subagent pack --pack-destination "$BOOTSTRAP_DIR"
BOOTSTRAP_TARBALL="$(find "$BOOTSTRAP_DIR" -name '*.tgz' -print -quit)"
npm publish "$BOOTSTRAP_TARBALL" --access public --tag rc
npm view @parallel-web/dsh-web-search dist-tags --json
npm view @parallel-web/dsh-responses-subagent dist-tags --json
```

The npm owner should inspect the tarball listing before the publish and complete npm's 2FA prompt.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Monorepo for @parallel-web npm packages.
## Packages

- [`@parallel-web/ai-sdk-tools`](./packages/ai-sdk-tools) - AI SDK tools for Parallel Web
- [`@parallel-web/dsh-responses-subagent`](./packages/dsh-responses-subagent) - Parallel Responses research subagent for DeepSeek Harness
- [`@parallel-web/dsh-web-search`](./packages/dsh-web-search) - Parallel Search provider for DeepSeek Harness
- [`@parallel-web/opencode-plugin`](./packages/opencode-plugin) - Opencode plugin for Parallel Web
- [`@parallel-web/pi-extension`](./packages/pi-extension) - pi agent extension for Parallel Web
Expand Down
63 changes: 63 additions & 0 deletions packages/dsh-responses-subagent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Parallel Responses for DeepSeek Harness

This DeepSeek Harness plugin adds `parallel_research`, an opt-in
[Parallel Responses](https://docs.parallel.ai/responses-api/examples/research-subagent)
subagent that returns complete, cited web research. It works alongside
`web_search` without changing the existing Parallel Search plugin.

Only the explicitly delegated question is forwarded as research input. Parent
history, files, workspace state, tools, and environment details are never
gathered automatically.

## Install

This package has not been published to npm. Install from the repository root:

```sh
pnpm --filter @parallel-web/dsh-responses-subagent build
pnpm --dir packages/dsh-responses-subagent pack --pack-destination /tmp
npx --yes @deepseek-ai/dsh@0.1.1-rc.2 \
plugin --profile web add \
/tmp/parallel-web-dsh-responses-subagent-0.1.0-rc.0.tgz
```

Provide your [Parallel API key](https://platform.parallel.ai/) in the terminal
that starts Harness, then configure a parent model in **Settings > Models**:

```sh
export PARALLEL_API_KEY="your-key"
npx --yes @deepseek-ai/dsh@0.1.1-rc.2 web
```

After the first npm release, replace the tarball path in the install command
with `@parallel-web/dsh-responses-subagent@rc`.

## Research depth and parallelism

Ask the parent agent to delegate a complete, standalone research question. It
receives guidance to preserve constraints, prioritize primary sources, and
cite the returned URLs.

To choose the research depth, edit the `subagent-parallel-responses` entry in
`~/.dsh/profiles/web/cordis.patch.yml`:

```yaml
- id: subagent-parallel-responses
config:
effort: low
```

Choose `low` for focused questions, `medium` (the default) for ordinary
research, or `high` for deeper synthesis. At low effort, the parent is
encouraged to dispatch independent questions together. Harness schedules
parallel tool calls itself, allowing 10 by default. To allow more, increase
**Settings > Plugins > Agent loop > Parallel tool calls**.

Keep `PARALLEL_API_KEY` in the launch environment, not in this profile.

## Remove

```sh
npx --yes @deepseek-ai/dsh@0.1.1-rc.2 \
plugin --profile web remove @parallel-web/dsh-responses-subagent
```
11 changes: 11 additions & 0 deletions packages/dsh-responses-subagent/cordis.patch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- insert:
- id: subagent-parallel-responses
name: '@parallel-web/dsh-responses-subagent'

- id: tool-subagent-parallel-responses
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: parallel-responses
toolName: parallel_research
enableRunInBackground: false
maxDepth: provider-managed
77 changes: 77 additions & 0 deletions packages/dsh-responses-subagent/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"name": "@parallel-web/dsh-responses-subagent",
"description": "Parallel Responses research subagent for DeepSeek Harness",
"version": "0.1.0-rc.0",
"author": "Parallel Web",
"license": "MIT",
"type": "module",
"sideEffects": false,
"engines": {
"node": "^22.19.0 || >=24.0.0"
},
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
},
"./package.json": "./package.json"
},
"files": [
"lib/index.js",
"lib/index.d.ts",
"cordis.patch.yml",
"README.md"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/parallel-web/parallel-npm-packages.git",
"directory": "packages/dsh-responses-subagent"
},
"keywords": [
"deepseek-harness",
"dsh-plugin",
"parallel-responses",
"research-agent"
],
"dsh": {
"bundle": {
"patch": "./cordis.patch.yml"
}
},
"scripts": {
"build": "tsdown",
"check": "pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run build",
"clean": "rm -rf lib",
"lint": "oxlint src tests tsdown.config.ts",
"prepare": "pnpm run build",
"test": "vitest run",
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"@deepseek-ai/cordis": "4.0.1",
"@deepseek-ai/dsh-launch-environment": "^0.1.0-rc.6 || ^0.1.1-rc.2",
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.1.1-rc.2",
"@deepseek-ai/dsh-subagent": "^0.1.0-rc.6 || ^0.1.1-rc.2",
"@deepseek-ai/dsh-tool-subagent": "^0.1.0-rc.6 || ^0.1.1-rc.2"
},
"dependencies": {
"@deepseek-ai/schemastery": "3.18.1"
},
"devDependencies": {
"@deepseek-ai/cordis": "4.0.1",
"@deepseek-ai/dsh-launch-environment": "0.1.0-rc.6",
"@deepseek-ai/dsh-session": "0.1.0-rc.6",
"@deepseek-ai/dsh-subagent": "0.1.0-rc.6",
"@deepseek-ai/dsh-tool-subagent": "0.1.0-rc.6",
"@types/node": "26.2.0",
"oxlint": "1.76.0",
"tsdown": "0.22.2",
"typescript": "6.0.3",
"vitest": "4.1.8"
}
}
83 changes: 83 additions & 0 deletions packages/dsh-responses-subagent/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/** Parallel Responses research-subagent provider for DeepSeek Harness. */

import type { Context } from '@deepseek-ai/cordis';
import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment';
import z from '@deepseek-ai/schemastery';
import type {} from '@deepseek-ai/dsh-subagent';
import {
PARALLEL_RESPONSES_EFFORTS,
ParallelResponsesProvider,
type ParallelResponsesEffort,
} from './provider.ts';

export { PARALLEL_RESPONSES_PROVIDER_ID } from './provider.ts';
export type { ParallelResponsesEffort } from './provider.ts';

export const name = 'subagent-parallel-responses';
export const inject = ['subagents', 'systemPrompt'];

const RESEARCH_TOOL_GUIDANCE =
'parallel_research delegates to an autonomous web-research specialist and ' +
'returns a synthesized, citation-backed answer. Pass a complete, ' +
'self-contained research question with all requested constraints. Do not ' +
'invent JSON schemas or machine-output requirements. ';

function researchToolGuidance(effort: ParallelResponsesEffort): string {
const strategy =
effort === 'low'
? 'Break multi-part research into focused, independent questions and ' +
'start those calls together when possible. Reconcile their answers ' +
'and make focused follow-up calls only for missing evidence.'
: 'For a connected question, prefer one complete handoff and use ' +
'focused follow-up calls only for material unresolved facts.';
return `${RESEARCH_TOOL_GUIDANCE}${strategy} Preserve and cite the returned source URLs.`;
}

/** Deployment configuration for the fixed Parallel Responses provider. */
export interface Config {
/** Explicit Parallel API key; omission uses the Harness launch snapshot. */
apiKey?: string;
/** Research tier; higher effort trades cost for deeper investigation. */
effort?: ParallelResponsesEffort;
}

export const Config: z<Config> = z.object({
apiKey: z.string().role('secret'),
effort: z.union(PARALLEL_RESPONSES_EFFORTS),
});

/**
* Register the fixed `parallel-responses` provider.
* @param ctx - context carrying the shared subagent service.
* @param config - optional explicit Parallel credential.
*/
export function apply(ctx: Context, config: Config): void {
const apiKey =
config.apiKey ??
launchEnvironmentOf(ctx).get('PARALLEL_API_KEY')?.value ??
'';
if (apiKey.trim().length === 0) {
throw new Error(
'dsh-responses-subagent: apiKey or PARALLEL_API_KEY is required'
);
}
ctx.subagents.registerProvider(
new ParallelResponsesProvider({
apiKey,
...(config.effort === undefined ? {} : { effort: config.effort }),
onError: (error) => {
ctx.logger.warn(
`subagent-parallel-responses: remote run failed: ${error.message}`
);
},
})
);
ctx.systemPrompt.section({
name: 'tool:parallel_research',
order: 110.5,
text: (context) =>
ctx.get('tools')?.get('parallel_research', context.scope) === undefined
? ''
: researchToolGuidance(config.effort ?? 'medium'),
});
}
Loading
Loading