-
Notifications
You must be signed in to change notification settings - Fork 2.4k
feat(dsh): DeepSeek Harness integration — connect adapter + cordis plugin #1209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
hejiawow
wants to merge
3
commits into
rohitg00:main
Choose a base branch
from
hejiawow:feat/dsh-integration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
648f123
feat(dsh): DeepSeek Harness integration — connect adapter + cordis pl…
hejiawow 0a234ef
fix(dsh): address CodeRabbit review — force dedup, DSH_HOME, security…
hejiawow 57a0edd
fix(dsh): guard JSON.stringify undefined in approval truncation (Code…
hejiawow File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # agentmemory × DeepSeek Harness (dsh) — Integration Design | ||
|
|
||
| ## Background | ||
|
|
||
| dsh (DeepSeek Harness) is a cordis-based agent harness with no long-term memory: session transcripts and compaction summaries stay per-session and are not semantically retrievable. This PR closes the gap using agentmemory's existing surfaces — the REST lifecycle endpoints (`/session/start`, `/observe`, `/context`, `/session/end`, `/remember`) and the MCP shim (`@agentmemory/mcp`) — mirroring the OpenCode plugin pattern (`plugin/opencode/`). | ||
|
|
||
| ## Architecture (four layers) | ||
|
|
||
| | Layer | What | Where | | ||
| |---|---|---| | ||
| | L1 MCP bridge | `mcp__agentmemory__*` tools via `@deepseek-ai/dsh-mcp-client` (stdio → `@agentmemory/mcp` shim, proxies to the daemon; reduced local fallback when unreachable) | `~/.dsh/profiles/<p>/cordis.patch.yml` entry, written by `agentmemory connect dsh` | | ||
| | L2 Behavior guidance | memory-usage guideline injected into every session (`~/.dsh/AGENTS.md`, read by dsh-agent-instructions) + `agentmemory-sync` skill (`~/.dsh/skills/`) | `src/cli/connect/guidelines.ts` + adapter | | ||
| | L3 Auto-capture plugin | `@agentmemory/dsh` cordis plugin: session lifecycle → REST | `plugin/dsh/` | | ||
| | L4 Deep collaboration | per-agent isolation (`agentId`), compaction bridge, team memory | plugin config / future | | ||
|
|
||
| ## Event mapping: agentmemory hooks ↔ dsh events | ||
|
|
||
| The plugin mirrors the official Claude Code hook semantics 1:1 on dsh's event stream: | ||
|
|
||
| | agentmemory hook (Claude Code) | dsh event | Form | | ||
| |---|---|---| | ||
| | SessionStart | `session/created` → `POST /agentmemory/session/start` | injecting (await + timeout + fail silent) | | ||
| | SessionStart stdout injection | `agent/pre-step` (step 1) middleware → first-step batch fold (idempotent per session) | injecting | | ||
| | UserPromptSubmit | `session/event` (`user/message`) → `/observe prompt_submit` | telemetry (fire-and-forget) | | ||
| | PreToolUse (matcher) | `session/event` (`tool/call`) → `/observe post_tool_use` (`mcp__agentmemory__*` self-calls filtered) | telemetry | | ||
| | Notification | `approval/asked` → `/observe notification` (allowlisted fields) | telemetry | | ||
| | Stop / SessionEnd | `session/disposed` → `POST /agentmemory/session/end` | telemetry (30s, promise-tracked) | | ||
| | PreCompact | `compaction/summary` → `POST /agentmemory/remember` (compaction bridge) | telemetry | | ||
|
|
||
| Event names are taken from `@deepseek-ai/dsh-session` / `@deepseek-ai/dsh-agent` (`session/created`, `session/disposed`, `session/event` stream types `user/message`/`tool/call`/`tool/result`, `agent/pre-step` middleware with `(_assembly, _context, next)`-style chaining, `approval/asked`, `compaction/summary`). | ||
|
|
||
| ## Design contract (same as the official hooks) | ||
|
|
||
| - Injecting handlers **await + time out + fail silently**; they never throw into cordis (`signal.aborted` returns the unmodified decision). | ||
| - Telemetry handlers are **fire-and-forget** and never block the agent loop; in-flight promises are tracked so nothing is dropped at teardown. | ||
| - Project resolution (`git rev-parse`) is cached per cwd — no child process per event; per-session `{cwd, project}` captured at `session/created`. | ||
| - Zero runtime dependencies: the plugin is a thin REST bridge (Node built-ins only), so dsh `file:` consumers need no build step; `lib/` is committed (repo convention, cf. `plugin/scripts/*.mjs`). | ||
|
|
||
| ## Verification (live, macOS) | ||
|
|
||
| - `npm test`: 1620 passed, 6 failed (all pre-existing environment failures in `embedding-provider.test.ts`, unrelated to this change), 1 skipped out of 1627 total. | ||
| - `@agentmemory/mcp` stdio handshake: initialize + `tools/list` → 53 tools; `memory_sessions` returns real daemon data. | ||
| - dsh `session.create` → daemon registers a session with `agentId=dsh`; observations captured during active sessions. | ||
| - `~/.dsh/AGENTS.md` guideline injected into live dsh sessions; `agentmemory-sync` skill picked up by dsh's skill registry. | ||
|
|
||
| ## Known environment caveat | ||
|
|
||
| A root-owned `~/.npm/_cacache` (npm historical bug) makes `npx -y @agentmemory/mcp` fail with EPERM, so the dsh MCP bridge cannot spawn the shim. Fix: `sudo chown -R "$(id -u):$(id -g)" ~/.npm` (never hard-code UID/GID), or point `--cache` at a private per-user directory in the entry's `args` (the installer generates `~/.cache/npmcache-dsh`; both documented in `plugin/dsh/install/cordis.patch.yml`). | ||
|
|
||
| ## Installer | ||
|
|
||
| `scripts/dsh-install.cjs` applies L1+L2 and declares the L3 plugin dependency idempotently (`--dry-run` preview, `--no-plugin` for config-only). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| # @agentmemory/dsh — agentmemory for DeepSeek Harness | ||
|
|
||
| A cordis plugin that connects agentmemory's long-term memory to DeepSeek Harness (dsh). It registers sessions on start, injects recalled context into the first step, captures user messages and tool calls, bridges compaction summaries into the memory store, and summarizes sessions on dispose — mirroring the official agentmemory hooks for Claude Code on dsh's event stream. | ||
|
|
||
| | agentmemory hook (Claude Code) | This plugin (dsh event) | Form | | ||
| |---|---|---| | ||
| | SessionStart | `session/created` → `POST /agentmemory/session/start` | injecting (await + timeout + fail silent) | | ||
| | SessionStart stdout injection | `agent/pre-step` (step 1) middleware → first-step batch fold | injecting | | ||
| | UserPromptSubmit | `session/event` (`user/message`) → `/observe prompt_submit` | telemetry (fire-and-forget) | | ||
| | PreToolUse (matcher) | `session/event` (`tool/call`) → `/observe post_tool_use` (filters `mcp__agentmemory__*` self-calls) | telemetry | | ||
| | Notification | `approval/asked` → `/observe notification` (allowlisted fields) | telemetry | | ||
| | Stop / SessionEnd | `session/disposed` → `POST /agentmemory/session/end` | telemetry (30s, tracked) | | ||
| | PreCompact | `compaction/summary` → `POST /agentmemory/remember` (compaction bridge) | telemetry | | ||
|
|
||
| ## Install | ||
|
|
||
| Prerequisite: the agentmemory daemon is running (`npx @agentmemory/agentmemory`, REST `http://localhost:3111`). | ||
|
|
||
| ### 1. MCP bridge (tools, optional but recommended) | ||
|
|
||
| Append the `mcp-agentmemory` entry from `install/cordis.patch.yml` to `~/.dsh/profiles/<profile>/cordis.patch.yml` (HMR hot-reloads it — no restart). dsh agents then get `mcp__agentmemory__*` tools. | ||
|
|
||
| ### 2. Plugin (auto-capture) | ||
|
|
||
| ```bash | ||
| dsh plugin --profile <profile> add @agentmemory/dsh | ||
| ``` | ||
|
|
||
| Development (local repo): | ||
|
|
||
| ```bash | ||
| # add to ~/.dsh/profiles/web/package.json dependencies: | ||
| # "@agentmemory/dsh": "file:/path/to/agentmemory/plugin/dsh" | ||
| cd ~/.dsh/profiles/web && pnpm install | ||
| ``` | ||
|
|
||
| Then append to `cordis.patch.yml` (defaults shown; override as needed): | ||
|
|
||
| ```yaml | ||
| - insert: | ||
| - id: agentmemory | ||
| name: '@agentmemory/dsh' | ||
| config: | ||
| url: http://localhost:3111 # agentmemory REST | ||
| secret: '' # match the daemon AGENTMEMORY_SECRET | ||
| agentId: dsh # per-agent memory isolation | ||
| injectInstructions: true # inject memory-tool guidance on first turn | ||
| injectContext: true # inject recalled project context on first turn | ||
| injectMaxChars: 6000 # injection budget (≈2k tokens) | ||
| observeToolCalls: true # capture tool calls as observations | ||
| compactionBridge: true # persist compaction summaries as memories | ||
| summarizeOnDispose: true # LLM summary on session dispose | ||
| ``` | ||
|
|
||
| Restart dsh (or wait for HMR to load the new plugin). | ||
|
|
||
| ### 3. Behavior guidance (optional) | ||
|
|
||
| - Global guidance: `install/AGENTS.md` → `~/.dsh/AGENTS.md` (auto-injected into every session) | ||
| - Memory skill: `install/skills/agentmemory-sync/` → `~/.dsh/skills/agentmemory-sync/` | ||
|
|
||
| ## Verify | ||
|
|
||
| 1. The first turn of a new session should show injected recalled context/guidance. | ||
| 2. `curl http://localhost:3111/agentmemory/sessions` lists the dsh sessions. | ||
| 3. After ending a session, `curl http://localhost:3111/agentmemory/search -H 'Content-Type: application/json' -d '{"query":"<what you did>"}'` recalls the new memory (add `-H "Authorization: Bearer $AGENTMEMORY_SECRET"` when the daemon requires auth). | ||
|
|
||
| ## Development | ||
|
|
||
| ```bash | ||
| npm run build # tsdown → lib/index.js (zero runtime dependencies) | ||
| npm test # vitest (20 cases: REST client / event mapping / injection / self-call filter / fail-open) | ||
| ``` | ||
|
|
||
| Design contract (same as the official hooks): injecting handlers await + time out + fail silently; telemetry handlers fire-and-forget and never block the agent loop; REST failures never throw into cordis (with `AGENTMEMORY_DSH_DEBUG=1` they are logged via the REST client). | ||
|
|
||
| ## Config | ||
|
|
||
| | Field | Default | Description | | ||
| |---|---|---| | ||
| | `url` | `http://localhost:3111` | agentmemory REST base URL | | ||
| | `secret` | empty | Bearer auth (only if the daemon sets `AGENTMEMORY_SECRET`) | | ||
| | `agentId` | `dsh` | memory owner agent; isolation key under `AGENTMEMORY_AGENT_SCOPE=isolated` | | ||
| | `injectInstructions` | `true` | inject memory-tool guidance on the first turn | | ||
| | `injectContext` | `true` | inject `/context` recalled project context on the first turn | | ||
| | `injectMaxChars` | `6000` | total injection budget in characters | | ||
| | `observeToolCalls` | `true` | `tool/call` → `post_tool_use` observations | | ||
| | `compactionBridge` | `true` | `compaction/summary` → `/remember` | | ||
| | `summarizeOnDispose` | `true` | `session/disposed` → `/session/end` (LLM summary) | | ||
|
|
||
| ## Build artifacts | ||
|
|
||
| `lib/index.js` (and the hand-written `lib/index.d.ts`) are committed alongside the source: dsh `file:` consumers load `lib/` directly with no publish-time build. After changing `src/index.ts`, run `npm run build` and commit the new `lib/` (the repo already commits build output, cf. `plugin/scripts/*.mjs`). | ||
|
|
||
| ## Limitations | ||
|
|
||
| - Event payload fields follow the running dsh version (`session/created`/`disposed` carry the session object; `agent/pre-step` is middleware; `session/event` stream events are `{type, data}`). | ||
| - `approval/asked` observations are skipped when the payload has no `sessionId`. | ||
| - The plugin only bridges REST; the MCP tools still need step 1's bridge. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| ## Agent memory (agentmemory) | ||
|
|
||
| You have persistent long-term memory via the agentmemory MCP server. Tools: `mcp__agentmemory__memory_recall`, `memory_smart_search`, `memory_save`, `memory_sessions`. | ||
|
|
||
| - At the START of a task, call `memory_recall` (or `memory_smart_search`) to load relevant past decisions, fixes, and user preferences; do not re-ask. | ||
| - When you learn something durable (a decision, a fix, a gotcha, a preference, a project convention), call `memory_save` to persist it. | ||
| - Prefer recall over re-deriving; save concise reusable facts, not transcripts. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # ── agentmemory L1: MCP bridge (append into ~/.dsh/profiles/<profile>/cordis.patch.yml) ── | ||
| # Provides mcp__agentmemory__* tools to every DSH agent. The stdio shim | ||
| # proxies to the agentmemory daemon (AGENTMEMORY_URL) and falls back to a | ||
| # reduced local tool set when the daemon is unreachable. | ||
| - insert: | ||
| - id: mcp-agentmemory | ||
| name: '@deepseek-ai/dsh-mcp-client' | ||
| config: | ||
| transport: stdio | ||
| serverName: agentmemory | ||
| command: npx | ||
| # If npx fails with EPERM (broken ~/.npm cache), point --cache at a | ||
| # private per-user dir, e.g. '~/.cache/npmcache-dsh' expanded to an | ||
| # absolute path by the installer (never /tmp — predictable + shared): | ||
| # args: ['--cache', '/home/<user>/.cache/npmcache-dsh', '-y', '@agentmemory/mcp'] | ||
| args: ['-y', '@agentmemory/mcp'] | ||
| env: | ||
| AGENTMEMORY_URL: http://localhost:3111 | ||
| # AGENTMEMORY_SECRET: '<match the daemon .env>' | ||
| # AGENTMEMORY_TOOLS: all # default: 8 core tools only (saves tokens) | ||
| toolCallTimeoutMs: 60000 | ||
| failOnStartupError: false | ||
|
|
||
| # ── agentmemory L3: cordis plugin (add after 'dsh plugin --profile <p> add @agentmemory/dsh') ── | ||
| # Note: new entries must be wrapped in an insert list; a top-level `- id:` only | ||
| # overrides the config of an existing entry. | ||
| - insert: | ||
| - id: agentmemory | ||
| name: '@agentmemory/dsh' | ||
| config: | ||
| url: http://localhost:3111 | ||
| secret: '' | ||
| agentId: dsh | ||
| injectInstructions: true | ||
| injectContext: true | ||
| observeToolCalls: true | ||
| compactionBridge: true | ||
| summarizeOnDispose: true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| name: agentmemory-sync | ||
| description: Sync with the agentmemory long-term memory at task start/end. Use when starting a new task, recalling past work, finishing a task, needing cross-session context, or reviewing what was done. | ||
| --- | ||
| # agentmemory memory sync | ||
|
|
||
| 1. At task start: call mcp__agentmemory__memory_recall with the task keywords + project path, format=compact, to bring relevant history into context. | ||
| 2. During the task: when you learn something durable (decision/fix/preference/convention), call memory_save immediately (type=fact, concepts: 2-5 keywords). | ||
| 3. At task end: call memory_save with a short outcome summary (type=insight), and confirm the session is registered via memory_sessions. | ||
| 4. For large handoffs: call memory_smart_search with expandIds for graph-diffusion recall, or use memory_lesson_save/memory_lesson_recall for lessons. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| export interface PluginContext { | ||
| on(event: string, listener: (...args: any[]) => unknown): void; | ||
| effect(callback: () => void | (() => void), label?: string): void; | ||
| logger: { info(...args: unknown[]): void; warn(...args: unknown[]): void; error(...args: unknown[]): void }; | ||
| } | ||
|
|
||
| export interface SessionLike { | ||
| id: string; | ||
| header?: { cwd?: string }; | ||
| } | ||
|
|
||
| // Event payloads vary by event type; consumers narrow data at runtime. | ||
| export interface SessionEvent { | ||
| type: string; | ||
| seq?: number; | ||
| data: any; | ||
| } | ||
|
|
||
| export interface AgentmemoryConfig { | ||
| url: string; | ||
| secret: string; | ||
| agentId: string; | ||
| injectInstructions: boolean; | ||
| injectContext: boolean; | ||
| injectMaxChars: number; | ||
| observeToolCalls: boolean; | ||
| compactionBridge: boolean; | ||
| summarizeOnDispose: boolean; | ||
| } | ||
|
|
||
| export interface RestClient { | ||
| post<T>(path: string, body: Record<string, unknown>, timeoutMs?: number): Promise<T | null>; | ||
| fire(path: string, body: Record<string, unknown>, timeoutMs?: number): void; | ||
| } | ||
|
|
||
| export function makeRestClient(url: string, secret: string, debug?: boolean): RestClient; | ||
| export function resolveProjectName(cwd: string, env?: Record<string, string | undefined>): string; | ||
| export function isAgentmemoryTool(name: string): boolean; | ||
| export function eventTextContent(content: unknown): string; | ||
| export function userMessagePrompt(event: SessionEvent, maxChars: number): string | null; | ||
| export function toolCallObservation(event: SessionEvent, maxChars: number): Record<string, unknown> | null; | ||
| export function compactionSummary(event: SessionEvent, maxChars: number): string | null; | ||
|
|
||
| export const name: string; | ||
| export function apply(ctx: PluginContext, config?: Partial<AgentmemoryConfig>): void; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: rohitg00/agentmemory
Length of output: 50376
🏁 Script executed:
Repository: rohitg00/agentmemory
Length of output: 12458
🏁 Script executed:
Repository: rohitg00/agentmemory
Length of output: 769
Use canonical MCP tool names in both installed guidance files.
The dsh bridge exposes
mcp__agentmemory__*tools. Barememory_*names have no supported alias and can cause the documented calls to fail. Prefix every tool reference in both files withmcp__agentmemory__.📍 Affects 2 files
plugin/dsh/install/AGENTS.md#L3-L6(this comment)plugin/dsh/install/skills/agentmemory-sync/SKILL.md#L7-L10🤖 Prompt for AI Agents