{"_id":"@creait/dsh-research-mode","_rev":"2-e737b2ba293f52a6f2263b6cf3d7ea16","name":"@creait/dsh-research-mode","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@creait/dsh-research-mode","version":"0.1.0","keywords":["dsh","deepseek-harness","cordis","plugin","agent-preset","mode","research","deep-research","workflow","multi-agent"],"author":{"name":"Francesco G","email":"francesco@creait.nl"},"license":"MIT","_id":"@creait/dsh-research-mode@0.1.0","maintainers":[{"name":"creait","email":"francesco@creait.nl"}],"homepage":"https://github.com/CREAIT-nl/dsh-plugins/tree/main/research-mode#readme","bugs":{"url":"https://github.com/CREAIT-nl/dsh-plugins/issues"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-api-remotes"],"platform":"web"}},"dist":{"shasum":"31c58b7abf72329ae542b71e1e6a3e2b31f2730c","tarball":"https://registry.npmjs.org/@creait/dsh-research-mode/-/dsh-research-mode-0.1.0.tgz","fileCount":16,"integrity":"sha512-76vO7RQOLnCVbEoUPvpRET2w081Z6MDmgq4aTCocdNYqBpVnE2gVOxiZoO5ZSTIOpRyU5av7Gs0nrLIO1e08WQ==","signatures":[{"sig":"MEYCIQCvz5iLqYPsuw7HPOkvrY5wdO8AGzMVexAQWAPcZjaPXQIhAIsKWsyUvX+BdYjIB5tTGiYpHGdg1l2c4uObW9Ug3rVU","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":112813},"main":"lib/index.js","type":"module","engines":{"node":">=20"},"exports":{".":{"default":"./lib/index.js"},"./tool":{"default":"./lib/tool.js"},"./client":"./client/client.cjs","./script":{"default":"./lib/script.js"},"./schemas":{"default":"./lib/schemas.js"},"./package.json":"./package.json"},"gitHead":"488ad11d2a6582854dc98b84feacf32e0a2d2337","scripts":{"test":"node --test test/*.test.js"},"_npmUser":{"name":"creait","email":"francesco@creait.nl"},"repository":{"url":"git+https://github.com/CREAIT-nl/dsh-plugins.git","type":"git","directory":"research-mode"},"_npmVersion":"11.11.0","description":"Deep research as an agent mode for DeepSeek Harness: a fixed, reviewed research loop that plans, researches in adaptive parallel rounds, synthesises and reviews — and only exists inside its own preset.","directories":{},"_nodeVersion":"25.8.1","dependencies":{"@deepseek-ai/schemastery":"^3.18.1","@deepseek-ai/dsh-settings":"^0.1.0-rc.6"},"_hasShrinkwrap":false,"peerDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-workflow":"^0.1.0-rc.8"},"_npmOperationalInternal":{"tmp":"tmp/dsh-research-mode_0.1.0_1787577864381_0.1995092102297531","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@creait/dsh-research-mode","description":"Deep research as an agent mode for DeepSeek Harness: a fixed, reviewed research loop that plans, researches in adaptive parallel rounds, synthesises and reviews — and only exists inside its own preset.","version":"0.2.0","type":"module","main":"lib/index.js","exports":{".":{"default":"./lib/index.js"},"./tool":{"default":"./lib/tool.js"},"./script":{"default":"./lib/script.js"},"./schemas":{"default":"./lib/schemas.js"},"./client":"./client/client.cjs","./package.json":"./package.json"},"dsh":{"bundle":{"patch":"./cordis.patch.yml"},"client":{"inject":["@deepseek-ai/dsh-client-runtime","@deepseek-ai/dsh-client-locale","@deepseek-ai/dsh-api-remotes"],"platform":"web"}},"license":"MIT","engines":{"node":">=20"},"scripts":{"test":"node --test test/*.test.js"},"dependencies":{"@deepseek-ai/dsh-settings":"^0.1.0-rc.6","@deepseek-ai/schemastery":"^3.18.1"},"peerDependencies":{"@deepseek-ai/cordis":"^4.0.1","@deepseek-ai/dsh-workflow":"^0.1.0-rc.8"},"keywords":["dsh","deepseek-harness","cordis","plugin","agent-preset","mode","research","deep-research","workflow","multi-agent"],"repository":{"type":"git","url":"git+https://github.com/CREAIT-nl/dsh-plugins.git","directory":"research-mode"},"homepage":"https://github.com/CREAIT-nl/dsh-plugins/tree/main/research-mode#readme","bugs":{"url":"https://github.com/CREAIT-nl/dsh-plugins/issues"},"author":{"name":"Francesco G","email":"francesco@creait.nl"},"gitHead":"c6086194b046a24cdd782ddba46961151a1e5388","_id":"@creait/dsh-research-mode@0.2.0","_nodeVersion":"24.19.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-QFTCfD7sdnpCjKxhTPa5TP6JmMHFYLd/b4I/025VRbU69mOEUy4DogVVngP7agZVkea/6WvVYnBtKvrMDq/dnQ==","shasum":"bf66753b0de253f4b7d349e9f7fb69e14c703ed3","tarball":"https://registry.npmjs.org/@creait/dsh-research-mode/-/dsh-research-mode-0.2.0.tgz","fileCount":16,"unpackedSize":114357,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@creait%2fdsh-research-mode@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICBVoNXPht9DnMxLx/cNbCym1q0vJeMn0TC7SwbCy/UsAiArYZBW/wsy0MXM1PyTPQIF7qqzU0tcEwvweUxpSeyrUA=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a88379cb-04e5-450a-bf67-8f31ec4d9857"}},"directories":{},"maintainers":[{"name":"creait","email":"francesco@creait.nl"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-research-mode_0.2.0_1787733825866_0.2685061427755602"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-24T13:24:24.245Z","modified":"2026-08-26T08:43:46.752Z","0.1.0":"2026-08-24T13:24:24.542Z","0.2.0":"2026-08-26T08:43:46.006Z"},"bugs":{"url":"https://github.com/CREAIT-nl/dsh-plugins/issues"},"author":{"name":"Francesco G","email":"francesco@creait.nl"},"license":"MIT","homepage":"https://github.com/CREAIT-nl/dsh-plugins/tree/main/research-mode#readme","keywords":["dsh","deepseek-harness","cordis","plugin","agent-preset","mode","research","deep-research","workflow","multi-agent"],"repository":{"type":"git","url":"git+https://github.com/CREAIT-nl/dsh-plugins.git","directory":"research-mode"},"description":"Deep research as an agent mode for DeepSeek Harness: a fixed, reviewed research loop that plans, researches in adaptive parallel rounds, synthesises and reviews — and only exists inside its own preset.","maintainers":[{"name":"creait","email":"francesco@creait.nl"}],"readme":"# @creait/dsh-research-mode\n\nDeep research as an agent **mode** for DeepSeek Harness — a fixed, reviewed\nresearch loop that plans, researches in adaptive parallel rounds, synthesises a\ncited report and then argues with it. It exists only inside its own preset.\n\n## Why this exists\n\ndsh already ships every piece of a research fan-out: a workflow engine, a\nsubagent seam, `web_search`, and — with a fetch provider mounted — `web_fetch`.\nWhat it does not ship is the loop.\n\nYou can ask the model to write one, with the `workflow` tool. The problem is that\na research script authored fresh on every call re-earns the same structural\nmistakes on every call, and all of them are invisible from the outside: the\nreport still arrives, still reads well, and is quietly less than it claims.\n\nSo the loop ships here as a constant — `lib/script.js`, reviewed once — and the\nmodel supplies parameters to it.\n\nThe design (planner → adaptive rounds driven by the researchers' own declared\ngaps → synthesis → adversarial review) is ported from\n[`dsh-deep-research`](https://github.com/omdsh-dev/dsh-deep-research) by\nomdsh-dev (MIT), which got the structure right. Four things are fixed:\n\n| # | Upstream | Here |\n| --- | --- | --- |\n| 1 | Questions still queued when the round cap hit were dropped: not researched, not counted, not mentioned. | The planner is told its budget up front, and every question the budget never reached leaves the loop **by name** — into the result, into the report's own \"what this does not cover\" section, and into the coverage footer. |\n| 2 | Harvested leads were capped at the round width and the rest discarded, so a round where every researcher found something urgent lost most of it. | Every high-priority lead is queued. If the budget ends first it is reported as deferred, never dropped. |\n| 3 | The `seen` set was rebuilt inside the round loop, so a gap three consecutive researchers reported got researched three times. | One dedup set for the whole run. |\n| 4 | Supplying your own questions skipped planning entirely — no scope, no dimensions, no coverage audit. | Supplied questions are mandatory and used verbatim, and the planner does its job *on top of* them: it names the scope they imply and records what they miss. |\n\nIt is also written in English throughout, which the original is not.\n\n## The mode\n\nA mode is a dsh **agent preset**: a composition mounted per agent, not per\nprocess. Choosing `Research mode` in the picker gets you an agent with\n\n- **`deep_research`** — the loop, as one tool call\n- **`web_search` + `web_fetch`** — which the loop's own child agents inherit,\n  because they are spawned onto this same composition\n- **file tools** — to read source material and write the report somewhere it\n  survives the session\n- **todo**, **ask-user**, and compaction tuned so a report-sized tool result is\n  not pruned to death\n- **skills** — `skill-filesystem` + `tool-skill`, over the same default roots as\n  the shipped modes, so a source that only answers to a particular method can be\n  written down once and reused. Only skill *descriptions* sit in context; a body\n  is loaded when the agent calls the `skill` tool.\n\nand, deliberately, **no shell**, **no `workflow` tool**, no plan mode.\n\nEvery other mode is untouched. `deep_research` is not in their tool catalog and\nnone of the guidance above is in their context window — which is the whole point\nof shipping this as a mode rather than as a plugin that registers a tool\neverywhere.\n\n## Install\n\n```bash\ndsh plugin --profile web add @creait/dsh-research-mode\n```\n\nRestart dsh — the boot manifest is assembled at startup. The plugin copies its\npreset into `<dsh home>/.agent-presets/research/`, and `Research mode` appears in\nthe picker.\n\nThat directory is writable and the copy is yours to edit. The installer records\nwhat it wrote and **will not overwrite a preset you have changed**; on upgrade it\nleaves your version alone and says so in the log.\n\n### Prerequisite: a fetch provider\n\nThe preset mounts `tool-web` with `fetch: true`. dsh implements `web_fetch` in\nfull but ships no fetch provider, so without one every fetch call fails and the\nresearchers are capped at search snippets — which is worth considerably less\nthan being able to open the page.\n\n[`@creait/dsh-web-fetch`](../web-fetch) is that provider:\n\n```yaml\n- id: web\n  config:\n    fetchProvider: local\n\n- id: tool-web\n  config:\n    fetch: true\n\n- insert:\n    - id: web-fetch\n      name: '@creait/dsh-web-fetch'\n```\n\nIf you are knowingly running without one, set `fetch: false` in the preset.\n\nThe preset also raises `fetchMaxOutputChars` from its 200,000 default to 400,000.\n`tool-web` applies that cap to the *source* before turndown converts it, not to\nthe markdown that comes out, so on HTML it budgets pre-cleanup bytes — most of\nwhich the converter discards. At the default a long article reaches the\nresearcher as its first third, flagged truncated, and a lead gets spent chasing\nthe rest of a page it already had. Turndown compresses 5-10x, so the rendered\nmarkdown still lands well under the raised cap; the cost is roughly 10k tokens\non the few pages large enough to need it. A `text/plain` body skips the\nconverter and can arrive at the full cap.\n\n## The loop\n\n```\nPlan        one agent, structured output: scope, dimensions, sub-questions,\n            and the planner's own declared coverage gaps\nResearch    up to `width` agents per round, up to `rounds` rounds. Each returns\n            confirmed claims (with sources), uncertainties (with reasons), and\n            gaps (with priority). High-priority gaps are queued as the next\n            round's questions, behind the originals — breadth before depth.\nSynthesize  one agent, given every finding and the coverage accounting, writing\n            the report in `language` for `audience`\nReview      one agent checking the report against its own evidence for\n            fabricated citations, overclaiming, contradictions and coverage\n            honesty; a second applying the critique\n```\n\nStructured output at the plan and research stages is load-bearing. A sub-agent\nthat returns prose has to be parsed, and a parser over model prose is where a\nresearch pipeline quietly starts inventing things.\n\nEvery stage fails soft except the two that cannot: a dead planner and a run where\nevery researcher failed both raise, because there is nothing to write from. A\nsingle failed researcher is recorded as an unanswered question and the run\ncontinues.\n\n## Calling it\n\n```\ndeep_research({ topic: \"…\" })\n```\n\n| Argument | Default | Meaning |\n| --- | --- | --- |\n| `topic` | *required* | The question the report has to answer. The planner sees **this string and nothing else** about the conversation, so the constraints belong in it. |\n| `questions` | — | Sub-questions that must be researched, used verbatim and first. The planner still audits them. |\n| `rounds` | `3` | Adaptive rounds. Depth on what the research turns up. |\n| `width` | `4` | Questions researched in parallel per round. Breadth. A width pinned in the composer overrides whatever the model passes. |\n| `audience` | — | Who the report is for, when it changes what belongs in it. |\n| `language` | `English` | Report language. |\n| `review` | `true` | The adversarial pass. |\n\n`rounds` and `width` are clamped to the preset's `maxRounds` / `maxWidth` (6 and\n8), which the model cannot argue past.\n\n## Pinning the width\n\nResearch-mode sessions get a `width` control in the composer, beside the access\nmode. Leave it empty and the model picks a width per call, as above. Type a\nnumber and that number wins — it replaces the `width` argument the model passed,\nand is still clamped by `maxWidth`.\n\nThe pin exists because width is the one research parameter the model cannot\nreason about. It knows how broad the topic is; it does not know anything about\nthe deployment. Pinning it makes the brief a deployment decision rather than a\nper-call guess.\n\nIt is **not** the capacity mechanism — see [Width is not concurrency](#width-is-not-concurrency).\nPin the width to shape the report; capacity is gen-limit's job, not this one's.\n\nThe value is a deployment-wide setting, not a per-session one: it persists in\n`~/.dsh/settings.yaml` under the `dsh-research-mode` namespace and applies to\nevery research run until changed. The tool reads it at call time, so a change\ntakes effect on the next call without a restart.\n\n| | |\n| --- | --- |\n| Namespace | `dsh-research-mode` |\n| Key | `width` — `0` means no pin |\n| Route | `GET`/`POST` `/api/dsh-research-mode/config`, loopback only |\n\nThe control renders only in sessions running the `research` preset — including\non the new-session screen, as soon as Research mode is picked from the hero chip,\nso the width can be set before the first message.\n\n## Width is not concurrency\n\n`width` is how many questions a round *takes*, not how many researchers run at\nthe same moment. The script hands the engine the whole round at once, and what\ndecides how many of them generate simultaneously is not in this plugin at all.\n\nIt is [`@creait/dsh-gen-limit`](../gen-limit), which caps concurrent generating\n**sessions** per provider/model — and every subagent is its own session\n(`dsh-subagent` mints a fresh `SessionId` per child), so a round of eight\nresearchers wants eight slots and gets however many the limit allows. The rest\n**wait**: at capacity gen-limit queues a request FIFO rather than refusing it.\n\nSo any width works, at any capacity. A round of eight against a limit of three\nruns three, then three, then two — slower than a machine that could take eight,\nand identical in output. Width goes back to meaning what it reads like it means.\n\nSet the limit in gen-limit's settings page, per provider and model. That is the\nonly place it lives:\n\n| | |\n| --- | --- |\n| Namespace | `dsh-gen-limit` |\n| Key | `limits[].max` — concurrent generating sessions; `-1` is unlimited |\n| Also | `queueTimeoutMs` (how long a request waits before it is refused instead), `maxQueued` (how deep the line may get) |\n\n### Why not the workflow engine's `maxConcurrentAgents`\n\nThe engine has its own per-run cap and this preset deliberately leaves it unset.\nIt is the wrong instrument for this:\n\n- **It is per run.** `WorkflowExecution` is constructed per workflow run, so two\n  research runs at a cap of 3 are 6 concurrent generations. It cannot express a\n  machine-wide ceiling because it has never seen the other runs.\n- **It cannot see your chat.** An ordinary session's turns never touch the\n  workflow engine, so they are invisible to it and it to them.\n- **It duplicates a number you already set.** Your deployment's capacity is\n  declared once, in gen-limit. Writing it again in a preset file that ships with\n  this plugin means two copies to disagree the day you change one.\n\nSet it only if you are running research **without** gen-limit installed. Unset,\nit derives from CPU count (`min(16, cores - 2)`), which measures the wrong\nmachine — what runs out is generation slots on the backend, not cores here.\n\nRetry still earns its place, but for a narrower case than before. With gen-limit\nqueueing, a request at capacity waits instead of failing, so the retry waterfall\nis no longer what keeps a wide round alive. It covers what is left: a wait that\nruns out (`queueTimeoutMs`), a queue too deep to join (`maxQueued`), and the\nordinary transport failures that have nothing to do with capacity. Keep\n`GEN_CAPACITY_EXCEEDED` in `retryableCodes` — it is now rare rather than routine,\nand it means the backend has been saturated for a sustained period.\n\n## The coverage block\n\nThe result carries its own audit, and it is rendered under the report rather than\nleft in a structured field nobody reads:\n\n```\nResearched 9 of 12 planned questions over 3 rounds at a width of 4, plus 2 follow-ups surfaced during the run.\nEvidence: 41 sourced claims, 6 of them low-confidence, 8 recorded uncertainties.\n\nNever answered (3) — the report above does not cover these:\n- What does the enterprise tier actually cost at 500 seats?\n- …\n```\n\nThis is the part that makes the numbers honest. A report that arrives without\nthem reads as complete coverage of its topic, and the agent relaying it has no\nway to know it is not.\n\n## Config\n\nSet on the `research-mode-tool` row inside the preset:\n\n| Key | Default | Meaning |\n| --- | --- | --- |\n| `rounds` | `3` | Default rounds when the call names none |\n| `width` | `4` | Default parallel width |\n| `maxRounds` | `6` | Ceiling the model cannot exceed |\n| `maxWidth` | `8` | Ceiling the model cannot exceed |\n| `review` | `true` | Run the adversarial pass by default |\n| `language` | `English` | Default report language |\n| `toolName` | `deep_research` | Rename the tool |\n\n## Two entry points\n\n| Specifier | Plane | Injects | Does |\n| --- | --- | --- | --- |\n| `@creait/dsh-research-mode` | roster (profile bundle) | *nothing required*; reaches for `settings` and `webServer` through scoped injects | Installs the preset, registers the `dsh-research-mode` settings namespace, serves the width-pin route, and ships the composer control. Registers no tool, no prompt, no command — nothing the model can see. |\n| `@creait/dsh-research-mode/tool` | agent (inside the preset) | `tools`, `workflowEngine` | Registers `deep_research`. |\n\nThey are separate modules rather than one behind a config flag because Cordis\n`inject` is all-required and gates loading. The Web surface disables\n`workflow-worker-thread` on the host plane and each preset mounts its own, so a\nroster row declaring `workflowEngine` would wait forever for a service that\ncomposition never publishes — and the preset would never install, so the mode\nwould never appear.\n\nFor the same reason the tool row must sit **inside** the group carrying\n`isolate: { workflowEngine: true }`, alongside the `workflow-worker-thread` row.\nThe realm is entry-local and invisible to siblings outside the group.\n\nTo put `deep_research` in another mode, copy that group into its preset.\n\n## Tests\n\n```bash\nnode --test test/*.test.js\n```\n\nThe loop is tested the way the engine runs it — a `node:vm` context with the same\nsix globals (`agent`, `parallel`, `pipeline`, `phase`, `log`, `args`) and the same\n`(async () => { … })()` wrapper — with `agent` answering from a script instead of\na model. Each of the four fixes above has a test named after the flaw it pins\ndown, alongside the failure paths, the tool's clamping and result shaping, the\ncoverage renderer, and the installer's refusal to overwrite a locally edited\npreset.\n\n## Licence\n\nMIT. The loop's design is ported from\n[`dsh-deep-research`](https://github.com/omdsh-dev/dsh-deep-research) (MIT,\nCopyright (c) 2026 dsh2026), with thanks. See `LICENSE` for the full\nacknowledgement.\n","readmeFilename":"README.md"}