5분 AI.
TrendShift 딥다이브 · 2026-08-22 · Daily #2

DeepSeek Harness (dsh) 딥다이브
— "모든 것이 플러그인"인 오픈소스 에이전트 하네스

deepseek-ai/deepseek-harness는 DeepSeek AI가 만든 오픈소스 에이전트 하네스다. 하네스란 LLM(언어모델)을 실제로 일하는 에이전트로 만들어 주는 "런타임 층"을 말한다 — 모델을 부르고, 도구를 쥐여 주고, 대화·파일·세션을 관리하는 배선 전부다. dsh가 특별한 건 그 층을 통째로 잘게 쪼갰다는 점이다. 모델 어댑터, 도구 레지스트리, 세션 로그, 심지어 에이전트 루프 자체까지 전부 "플러그인"이라, 손댈 특권적 코어가 없고 무엇이든 설정만으로 갈아끼울 수 있다. 이 조립을 가능하게 하는 뼈대가 Cordis라는 플러그인 프레임워크다.
(저장소: deepseek-ai/deepseek-harness · TypeScript 모노레포 · MIT · 개발자 프리뷰 v0.1.1-rc.2 · 227개 패키지/47그룹 · Cordis 4.0.1 기반 · pnpm 11.7 · Node 22/24 · GitHub 166.6k★ · TrendShift Daily #2)
목차
  1. 프로젝트 한 줄 요약
  2. 왜 지금 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트
  7. 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한 줄 요약

"에이전트를 이루는 모든 부품을 교체 가능한 플러그인으로 쪼갠, DeepSeek의 오픈소스 에이전트 런타임"

DeepSeek Harness(명령어 이름 dsh)는 "LLM + 도구 + 루프"를 하나의 제품으로 묶는 에이전트 하네스다. Claude Code나 OpenAI Codex처럼 "터미널에서 도는 코딩 에이전트"를 떠올리면 된다. 하지만 그런 도구들이 대개 하나의 통짜 프로그램인 반면, dsh는 정반대 설계를 택했다. 에이전트를 이루는 조각 하나하나 — 어떤 모델을 쓸지, 어떤 도구를 쥘지, 파일을 어떻게 읽을지, 명령을 어디서 실행할지, 대화를 어떻게 저장할지 — 를 전부 독립된 플러그인으로 만들고, 부팅할 때 설정 파일 하나로 조립한다.

그래서 dsh의 공식 슬로건이 "Everything is a Plugin"(모든 것은 플러그인이다)이다. 모델 어댑터도 플러그인, 도구 레지스트리도 플러그인, 세션 로그도 플러그인, 에이전트의 핵심 루프(agent-loop)마저 플러그인이다. "여기가 심장부니까 함부로 못 고친다"는 특권적 코어가 아예 없고, 모든 부분이 설정으로 교체·제거·추가된다.

한눈에 이해하는 비유

"안드로이드 폰처럼, 런처·키보드·카메라 앱까지 전부 갈아끼울 수 있는 에이전트"

보통의 에이전트 도구는 아이폰에 가깝다 — 잘 만들어졌지만 기본 앱을 마음대로 바꾸긴 어렵다. 모델을 바꾸거나 도구를 끼우려면 그 프로그램의 코드를 직접 뜯어야 한다.

dsh는 안드로이드에 가깝다. "모델을 DeepSeek에서 다른 것으로", "파일 읽기를 로컬 대신 원격 샌드박스로", "기억을 SQLite 대신 JSONL로" 같은 교체가 코드 수정이 아니라 설정 한 줄로 끝난다. 부품이 다 규격화된 소켓(서비스)에 꽂히기 때문이다.

가장 빠른 체험은 설치 없이 한 줄이면 된다. 아래 명령은 로컬에 웹 UI를 띄운다.

# npm으로 바로 실행 (Node.js만 있으면 됨)
npx @deepseek-ai/dsh web
# → http://127.0.0.1:3080 에 웹 UI가 뜨고 브라우저가 열린다

단, 저장소는 스스로 개발자 프리뷰(v0.1.1-rc.2)임을 명시한다. README에 "호환성을 깨는 변경이 있을 것"이라 못 박아 두었으니, 지금은 "완성품"이라기보다 설계를 공부하고 플러그인을 실험하는 대상으로 보는 게 맞다. 그럼에도 규모는 이미 거대하다 — 227개 npm 패키지가 47개 그룹으로 나뉜 대형 TypeScript 모노레포다.

2왜 지금 주목받는가

"DeepSeek 공식 릴리스 + '특권적 코어 없음'이라는 급진적 설계 + 자기 자신을 고치는 에이전트"

비슷한 에이전트 하네스(Claude Code, Codex, LangGraph, OpenHands 등)는 이미 많다. 그런데 dsh가 공개되자마자 GitHub 별 16만 개를 넘기고 TrendShift 상위에 오른 건 네 가지가 겹쳤기 때문이다.

① DeepSeek AI의 공식 오픈소스

DeepSeek는 저비용 고성능 모델로 이름을 알린 곳이다. 그런 곳이 "우리 모델을 어떤 런타임 위에서 돌리길 기대하는가"를 코드로 공개했다는 점 자체가 신호다. 라이선스는 MIT로 완전 개방이고, 모델 어댑터(dsh-llm-deepseek)는 deepseek-v4-pro·deepseek-v4-flash를 겨냥하지만 구조상 어떤 모델도 붙일 수 있다.

② "특권적 코어가 없다"는 급진적 설계

대부분의 에이전트 프레임워크는 "고정된 엔진 + 확장 포인트" 구조다. 엔진은 못 건드리고, 정해진 훅에만 코드를 꽂는다. dsh는 그 발상을 뒤집어, 엔진조차 플러그인으로 만들었다. 에이전트 루프가 마음에 안 들면 루프 플러그인을 통째로 갈아끼우면 된다. 이 "끝까지 밀어붙인 조립식(composability)" 철학이 개발자들의 흥미를 끌었다.

③ 자기 자신을 실시간으로 고치는 에이전트

dsh에는 에이전트가 "지금 돌고 있는 자기 런타임"을 들여다보고, 새 플러그인을 즉석에서 짜서 장착·해제하는 도구(cordis_* 툴셋)가 들어 있다. 모델이 cordis_inspect로 현재 서비스·도구·이벤트를 살피고, cordis_define·cordis_run으로 자기가 쓸 새 도구를 만들어 mount하는 식이다(데모: pnpm run demo:cordis). "에이전트가 자기 능력을 스스로 확장한다"는 아이디어를 실물 코드로 보여 준 게 화제가 됐다.

④ 남의 에이전트까지 부하로 부린다 (멀티 백엔드 서브에이전트)

dsh는 하위 작업을 떼어 서브에이전트에게 시킬 때, 그 서브에이전트의 정체를 고를 수 있다. 같은 dsh를 fork하는 건 물론, Claude Code(공식 Agent SDK 경유), Codex, ACP(Agent Client Protocol) 호환 에이전트까지 하나의 하네스 아래에 묶는다. "여러 에이전트를 조율하는 상위 에이전트"를 표준 방식으로 만들 수 있다는 뜻이다.

도구강점dsh와의 차이
Claude Code완성도·안정성 높은 코딩 에이전트통짜 제품. 내부 루프·도구 교체는 제한적(dsh는 서브에이전트로 품음)
OpenAI Codex터미널 코딩 에이전트, 넓은 사용자층모델·루프 교체보다 "바로 쓰기" 중심. dsh는 백엔드로 편입 가능
LangGraph그래프로 에이전트 흐름 설계라이브러리(직접 조립). dsh는 부팅·세션·샌드박스까지 갖춘 런타임
deepseek-harness모든 부품이 플러그인특권적 코어 없음 + 설정으로 전면 교체 + 자기수정 + 멀티 백엔드 서브에이전트

3기술 스택 전체 지도

"TypeScript 모노레포 · Cordis 프레임워크 · React 웹 UI · C 샌드박스 · Python SDK"

dsh는 TypeScript 일색의 모노레포다(소스 .ts 2,472개 + .tsx 262개). 전 구간이 ESM(ECMAScript 모듈)이고, 패키지 관리는 pnpm@11.7 워크스페이스, 실행 환경은 Node 22/24다. 모든 npm 패키지가 @deepseek-ai/dsh-* 이름을 달고, 하나같이 Cordis를 peerDependency로 요구한다 — 즉 "패키지 = 플러그인"이 규칙으로 강제된다. 스택은 "프레임워크 층", "빌드/품질 층", "런타임 표면(웹/네이티브/파이썬) 층"으로 나눠 보면 깔끔하다.

프레임워크 · 코어 층

요소 / 버전역할
Cordis 4.0.1이 프로젝트의 뼈대. 플러그인이 서비스·타입 이벤트·되돌릴 수 있는 효과를 공유 컨텍스트에 기여하는 프레임워크. vendor/cordis에 고정 동봉.
cosmokit 1.8.2 · schemastery 3.18.1Cordis 생태계 유틸. schemastery는 플러그인 Config타입 기반 스키마로 검증(잘못된 설정은 로드 시 즉시 실패).
core/* 패키지제품 API 척추: ctx.agents(agent) · ctx.agentLoop(agent-loop) · ctx.tools(tools) · ctx.sessions(session) · ctx.systemPrompt.
llm/* 패키지모델 어댑터. dsh-llm(중립 인터페이스) + dsh-llm-deepseek + dsh-llm-pi-ai(대체 백엔드) + dsh-llm-retry + dsh-token-meter.

빌드 · 품질 층 (Rust 계열 도구 다수)

요소 / 버전역할
TypeScript ^6.0.3host/client 두 프로그램으로 분리 빌드(tsconfig.host.json · tsconfig.client.json). 뒤 4번에서 이유 설명.
tsdown ^0.22 · tsx ^4.22번들러 + TS 즉시 실행기. dsh CLI는 tsx/esm 훅으로 소스를 바로 돈다.
vitest ^4.1 (+coverage-v8)테스트 러너. CI 게이트가 파일별 커버리지 100%를 요구한다. fast-check로 속성 기반 테스트도.
oxlint 1.76 (+tsgolint)eslint가 아니라 Rust로 작성된 초고속 린터. knip(미사용 코드)·jscpd(복붙 탐지)·publint·lefthook(git 훅) 동반.
typert (typert/*)타입 그래프 기반 RPC 생성기. host↔client·프로세스 경계의 통신 타입을 자동 생성.

런타임 표면 층 — 웹 · 네이티브 · 파이썬

요소역할
React + Vite (apps/web)웹 UI. 흥미롭게도 UI 자체가 ~50개 Cordis 플러그인(client/ui-*)으로 쪼개져, 슬롯(SlotMap) 레지스트리로 조립된다.
C11 Landlock 런처 (native/)Rust가 아닌 C11로 쓴 ~300줄 샌드박스 런처(musl 정적 링크). 리눅스 커널의 Landlock으로 프로세스를 스스로 가둔 뒤 명령을 exec.
Python SDK (python/sdk)pip install deepseek-harness-sdk. pydantic 2.12 기반. dsh를 서브프로세스로 띄워 JSON-RPC(stdio)로 조종한다.
샌드박스 백엔드리눅스 bwrap/Landlock · macOS Seatbelt · Windows ACL — 실패 시 닫힘(fail-closed)으로 안전하게 거부.
node-pty · @vscode/ripgrep영속 터미널(PTY) 세션 + 초고속 파일 검색(glob/grep). 셸·터미널·검색 도구의 뒷단.
이 스택이 말해 주는 것

스택 곳곳에서 두 가지 취향이 읽힌다. 첫째, "경계에서만 검증하고, 안에서는 타입을 믿는다" — schemastery로 설정을, typert로 프로세스 경계를 지키되, 같은 프로세스 안 타입 경계는 TypeScript를 신뢰한다. 둘째, "느린 도구를 빠른 도구로 교체" — eslint 대신 oxlint(Rust), 검색은 ripgrep, 샌드박스는 커널 기능(Landlock)을 직접 쓴다. 프리뷰 단계인데도 테스트 커버리지 100%를 CI로 강제할 만큼 엔지니어링 기준이 높다.

4아키텍처 심화 분석

"Cordis 컨텍스트에 서비스를 꽂고 · 이벤트로 통신하며 · 되돌릴 수 있는 효과로 조립한다"

dsh를 이해하는 열쇠는 밑에 깔린 Cordis다. Cordis의 핵심 발상은 다섯 문장으로 요약된다. ① 플러그인은 apply(ctx) 함수(또는 Service 클래스)다. ② 컨텍스트(ctx)는 서비스들의 저장소이고, 서비스는 ctx.tools처럼 안정적인 이름(키)을 차지한다. ③ 플러그인은 필요한 서비스를 inject로 선언하면, 그게 준비될 때까지 자동으로 기다린다(부팅 순서를 손으로 짤 필요 없음). ④ 통신은 타입이 붙은 이벤트로 한다. ⑤ 모든 등록은 ctx.effect()로 이뤄지는 되돌릴 수 있는 효과라, 리로드·해제가 깔끔하게 풀린다.

한 문장 정의
Context · Service · Effect
컨텍스트는 "부품이 꽂히는 메인보드", 서비스는 "메인보드의 규격 슬롯(ctx.llm, ctx.fs …)에 꽂힌 부품", 효과는 "꽂았다 뺐다 할 수 있는 연결". 다른 플러그인은 구체적 구현을 import하지 않고 슬롯 이름으로 부품을 찾는다. 그래서 부품을 바꿔 꽂아도 나머지가 그대로 동작한다.

실행 중인 dsh는 부팅 시 프로파일(profile)번들(bundle)들을 순서대로 쌓아 만든 하나의 플러그인 트리다. dsh-base가 항상 첫 층(모델·도구·저장·샌드박스·정책·설정·자격증명·텔레메트리)이고, dsh-web-app이 브라우저 앱을, dsh-headless가 일회성 러너를 얹는다. 무엇이 조립됐는지는 dsh --profile web --dump-config로 들여다볼 수 있다.

부팅 조립 (profile = 번들을 순서대로 쌓기) ┌─────────────────────────────────────────────────────────┐ │ profile: web (홈 디렉토리의 명명된 조합) │ │ └ 번들 순서: dsh-base → dsh-web-app → (오버레이) │ └─────────────────────────────────────────────────────────┘ │ cordis.yml (행: {id, name, config}) 로 mount ▼ Cordis Context (ctx) ── 서비스들이 이름으로 꽂힌 저장소 ├─ ctx.llm 모델 어댑터 (llm/dsh-llm-deepseek …) ├─ ctx.tools 도구 레지스트리 (tools + tool-fs/bash/web …) ├─ ctx.agents 에이전트 레지스트리 ├─ ctx.agentLoop 에이전트 루프 ← 이것마저 플러그인 ├─ ctx.sessions 세션 로그(이벤트 소싱) ├─ ctx.fs / ctx.shell / ctx.sandbox 능력 이음새(seam) └─ … (227개 패키지가 각자 서비스/이벤트/도구를 기여)

핵심 ① — 능력 이음새(seam): 하나 갈아끼우면 제품 전체가 바뀐다

dsh 설계의 중심 개념이 seam(이음새)이다. 하나의 능력은 항상 세 역할로 이뤄진다 — 서비스 정의(인터페이스), 서비스 제공자(구현), 소비자(보통 모델이 쥐는 도구). 예컨대 파일시스템은 ctx.fs(정의) + dsh-fs-local(제공자) + dsh-tool-fs(소비자)로 구성된다. 파일·셸·샌드박스 제공자가 하나의 실행 세계를 공유하도록 설계돼 있어, 이 제공자들을 원격 샌드박스로 가리키기만 하면 Bash·터미널·LSP가 통째로 그 원격으로 옮겨간다. "한 부품 교체로 제품 전체 동작이 바뀐다"는 게 이 구조의 힘이다.

핵심 ② — 이벤트로 흐르는 에이전트 루프 (턴과 스텝)

에이전트가 한 번 "생각→도구 사용→답변"을 도는 과정은 전부 타입 이벤트의 흐름이다. 스텝(step)은 모델 요청 1회 + 그에 딸린 도구 호출들이고, 턴(turn)은 0개 이상의 스텝이다. 아래가 실제 이벤트 순서다.

turn/start 다음 입력과 대기 메시지 1건을 집어 프롬프트+도구 스키마 조립 → agent/pre-step (거부 | 진입) step/start 들어온 메시지를 user/message 로그에 append 로그에서 모델 히스토리 파생(deriveMessages) agent/request → llm/stream → assistant/chunk* → assistant/message tool/call* → tools/pre-execute → tools/execute → tools/post-execute → tool/result* step/end → agent/turn-stopping turn/end

agent/pre-step·agent/request·llm/stream·tools/*워터폴(waterfall) 이벤트라, 리스너가 다음으로 넘기려면 반드시 next()를 불러야 한다(부르지 않으면 그 자리에서 결정을 가로챈다). 미들웨어처럼 "감싸기"가 되는 구조다. 새 정책(요청 가로채기, 도구 검열, 재시도)은 루프 코드를 고치는 게 아니라 이 이벤트에 리스너를 플러그인으로 붙여 넣는다.

중요한 불변식(invariant)
"모델에게 보이는 것은 곧 로그에 남은 것"
모델 요청에 들어가는 모든 것은 세션 로그로부터 재구성될 수 있어야 한다(deriveMessages()가 로그→히스토리로 투영). 그래서 모델에게 새 입력을 주려면 반드시 새 세션 이벤트를 남겨야 하고, 런타임이 이 규칙을 실제로 검사한다. 재현·감사·리플레이가 공짜로 따라오는 설계다.

핵심 ③ — 자기 참조: 살아 있는 런타임을 모델이 조작

dsh-tool-cordis는 모델에게 지금 돌고 있는 Cordis 런타임을 다루는 5개 도구를 준다. cordis_inspect(서비스·플러그인·도구·이벤트 관찰), cordis_define(모델이 짠 플러그인 등록 — host 코드와 브라우저 client 코드 양쪽), cordis_run(호스트 절반을 vm 샌드박스에서 평가), cordis_stop, cordis_undefine. 이렇게 만든 동적 플러그인은 프로세스 메모리에만 산다. 앞서 본 "되돌릴 수 있는 효과"가 있기에, 모델이 즉석에서 장착한 기능도 안전하게 해제된다.

알아 둘 것
개발자 프리뷰 — 호환성 보장 없음

README와 AGENTS.md 모두 프리뷰임을 강조한다. SESSION_FORMAT_VERSION은 아직 0이고 "호환성 셔임보다 올바른 토대를 우선한다"는 방침이라, 세션 포맷·API가 예고 없이 바뀔 수 있다. 외부 PR도 현재는 닫혀 있다(플러그인 생태계는 dsh-plugin 토픽으로 개방). 지금 시점의 dsh는 "프로덕션 투입"이 아니라 설계 학습과 플러그인 실험에 맞다.

5디렉토리 구조 해부

"packages/(227개 플러그인) · apps/(cli·web) · native/(C 샌드박스) · python/(SDK) · vendor/(Cordis)"

모노레포의 최상위는 역할별로 명확히 갈린다. 압도적 대부분의 코드가 packages/<그룹>/<패키지>/ 아래 잘게 흩어져 있는 게 특징이다.

deepseek-harness/ ├─ packages/ ★ 47개 그룹, 227개 플러그인 패키지 (모든 @deepseek-ai/dsh-*) │ ├─ core/ agent · agent-loop · tools · session · system-prompt · scope │ ├─ llm/ llm · llm-deepseek · llm-pi-ai · llm-retry · token-meter │ ├─ fs/ fs · fs-local · fs-sandbox · tool-fs · tool-fs-search │ ├─ shell/ shell · bash-local/sandbox · pwsh-* · tool-bash(-persistent) │ ├─ terminal/ terminal · terminal-bash · tool-terminal (영속 PTY) │ ├─ sandbox/ sandbox · sandbox-local(bwrap/landlock/seatbelt/ACL) · policy │ ├─ subagent/ claude-code · codex · dsh-sdk · fork/spawn-in-process …(11개) │ ├─ session/ jsonl · sqlite · checkpoint · projection · telemetry-otel …(14개) │ ├─ web/ tool-web(search/fetch) · web-search-deepseek/exa/perplexity │ ├─ client/ ★ 웹 UI 자체가 ~50개 플러그인 (ui-conversation, ui-sidebar …) │ ├─ extensions/ tool-cordis(자기수정) · cordis-host/client-runner │ ├─ host/ apiproxy · webserver · frontend-static · plugin-inventory │ └─ (그 외) mcp · lsp · storage · compaction · workflow · skill · plan · todo … ├─ apps/ │ ├─ cli/ @deepseek-ai/dsh — 'dsh' 명령의 진입점(프로파일·플러그인·web) │ └─ web/ Vite로 client-web 셸을 빌드 → dist/ 를 dsh web이 서빙 ├─ native/landlock-run/ C11 Landlock 런처(musl 정적) + per-platform 바이너리 ├─ python/ sdk(deepseek-harness-sdk) + sdk-runtime(단일 실행 바이너리) ├─ vendor/ cordis · cosmokit · schemastery · cordis-plugin-* (동봉·고정) ├─ examples/ headless-agent · jsonrpc-agent · web-cordis · mcp-memory · acp-agent ├─ docs/ architecture.md · development.md · cordis-* · cookbook · subsystems ├─ scripts/ 146개 .ts (빌드·릴리스·카탈로그 생성·검증 게이트) ├─ .agents/ notes/(에이전트 노트 원장) · skills/(레포 전용 스킬) └─ AGENTS.md 에이전트 규칙(루트/packages/examples의 CLAUDE.md는 이 파일의 심볼릭 링크)

구조가 곧 철학이다. 이 저장소에서 "기능을 추가한다"는 건 대부분 코드 흐름을 고치는 게 아니라 packages/ 아래에 새 패키지(=플러그인)를 하나 더 놓는 것이다 — 그래서 이 레포는 하나의 앱이 아니라 "플러그인이 자라는 플랫폼"에 가깝다. 새 모델은 ctx.llm에 어댑터를 등록하는 패키지로, 새 도구는 ctx.tools에 등록하는 패키지로 들어온다.

눈여겨볼 폴더
.agents/notes/ — "에이전트 노트" 원장
이 레포는 사람이 아니라 에이전트가 개발한다는 전제로 짜였다. .agents/notes/에는 implemented·proposed·rejected·archived로 나뉜 날짜별 결정 기록이 쌓이고, AGENTS.md는 "사소하지 않은 변경은 반드시 Agent Note를 함께 남겨라"고 규정한다. CLAUDE.mdAGENTS.md로 향하는 심볼릭 링크인 것도 같은 맥락 — 모든 에이전트에게 같은 규칙을 준다.

6학습 포인트

"이 저장소 하나로 배우는 것 — 플러그인 아키텍처·의존성 주입·이벤트 미들웨어·대규모 모노레포"

dsh는 "잘 만든 소프트웨어 아키텍처"의 교보재다. 에이전트에 관심이 없어도, 아래 주제를 실전 코드로 배울 수 있다.

① 플러그인 아키텍처와 의존성 주입(DI)

"고정 코어 + 확장 포인트"가 아니라 "전부 플러그인"이라는 극단을 실제로 어떻게 구현하는지 볼 수 있다. 서비스를 이름으로 찾고(ctx.tools), 필요한 것을 inject로 선언하면 프레임워크가 순서를 풀어 준다. 스프링(Java)이나 NestJS의 DI를 "왜, 어떻게"의 관점에서 다시 이해하게 된다.

② 되돌릴 수 있는 효과(reversible effect)와 생명주기

모든 등록을 ctx.effect()로 하면 "설치의 역순으로 자동 해제"가 공짜로 생긴다 — 플러그인을 껐다 켜도, 리로드해도, 자원이 새지 않는다. 이것이 Cordis가 말하는 "시공간적 조립성(spatiotemporal composability)"의 실체다. 리액트의 useEffect 클린업이 왜 중요한지 아는 사람이라면 이 개념이 프레임워크 전체로 확장된 모습을 보게 된다.

③ 이벤트 워터폴 = 미들웨어 패턴

에이전트 루프를 "고칠 수 없는 엔진"이 아니라 "next()로 넘기는 미들웨어 체인"으로 만든 설계는 Express·Koa의 미들웨어, 혹은 Redux 미들웨어와 정확히 같은 뼈대다. "가로채고, 감싸고, 넘긴다"는 패턴을 에이전트 문맥에서 다시 익힌다.

④ 능력 이음새(seam)로 테스트·이식성 확보

파일·셸·샌드박스를 인터페이스(정의)와 구현(제공자)으로 갈라 두면, 테스트에선 mock 제공자를, 운영에선 원격 샌드박스 제공자를 꽂을 수 있다. "구현이 아니라 인터페이스에 의존하라"는 원칙(의존성 역전)이 제품 규모에서 어떻게 이득을 내는지 체감할 수 있다.

⑤ 대규모 TypeScript 모노레포 운영

227개 패키지를 pnpm 워크스페이스로 묶고, host/client 두 tsconfig로 분리 빌드하는 이유(양쪽이 같은 Context 키를 서로 다른 서비스로 선언 병합하므로, 한 프로그램이 둘 다 보면 충돌)를 배운다. 커버리지 100% 게이트, oxlint, knip, jscpd, lefthook까지 "대형 레포를 무너지지 않게 지키는 장치"의 실물 사례다.

⑥ 커널 수준 샌드박싱 (Landlock in C)

에이전트가 함부로 파일을 못 건드리게 막는 native/landlock-run은 리눅스 Landlock LSM을 C로 직접 부른 ~300줄짜리 교과서다. "자신에게 규칙을 걸고 exec하면 그 제한이 자식 프로세스까지 상속된다"는 fail-closed 샌드박스 기법을 배운다.

7시스템 요구사항

"Node 22/24 · pnpm 11.7 · DeepSeek API 키 · (샌드박스용) 리눅스 커널 5.13+"

dsh 본체는 Node로 돌기 때문에 무거운 GPU가 필요 없다(모델 추론은 API로 위임). 대신 버전 요건이 다소 최신이다.

항목요구 / 참고
Node.js^22.19.0 || >=24.0.0 — 비교적 최신 LTS. npx @deepseek-ai/dsh web이면 바로 실행.
패키지 매니저소스 빌드 시 pnpm@11.7.0(모노레포 워크스페이스). npm/yarn 아님.
모델 자격증명DEEPSEEK_API_KEY(선택 DEEPSEEK_BASE_URL). 웹 UI 설정에서 입력하거나 루트 .env에 둔다.
웹 UI기본 http://127.0.0.1:3080. --no-open으로 브라우저 자동 열기 생략.
샌드박스(선택)Landlock은 리눅스 커널 5.13+ 필요(ABI 레벨로 full/partial 판정). 없으면 bwrap/macOS Seatbelt/Windows ACL로 대체, 다 안 되면 닫힘.
OSWindows/macOS/Linux 크로스 플랫폼. Windows 영속성엔 koffi·node-pty(ConPTY) 사용.
Python SDK(선택)python >=3.10 + pydantic 2.12. pip install deepseek-harness-sdk로 dsh를 서브프로세스로 조종.

8직접 해볼 수 있는 실습 과제

"웹 UI 한 번 띄우기부터, 나만의 Cordis 플러그인 작성까지 — 난이도별 5단계"
LEVEL 1 · 입문 첫 실행

웹 UI 띄우고 첫 작업 시키기

npx @deepseek-ai/dsh web으로 로컬 웹 UI를 띄운다. Settings → Models에서 DeepSeek API 키(platform.deepseek.com 발급)를 넣고, 작업 폴더를 고른 뒤 "이 폴더 요약해 줘" 같은 간단한 작업을 시켜 본다. 목표: 하네스가 모델·도구·작업 폴더를 어떻게 엮는지 전체 흐름을 몸에 익히기.

LEVEL 2 · 초급 헤드리스 · 설정 읽기

headless 프로파일로 일회성 실행 + cordis.yml 뜯어보기

소스를 클론해 pnpm install && pnpm run build 후, pnpm dsh --profile headless "summarize this workspace"로 일회성 세션을 돌린다. 그다음 examples/headless-agent/cordis.yml을 열어 어떤 플러그인이 {id, name, config} 행으로 조립됐는지 읽는다. 배우는 것: 프로파일·번들·cordis.yml이 실제로 무엇을 켜고 끄는가.

LEVEL 3 · 중급 오버레이 · MCP

--patch로 플러그인 얹기 (MCP 메모리 서버 연결)

examples/mcp-memory/를 참고해 dsh web --patch "$PWD/examples/mcp-memory/memorix.cordis.yml"로 외부 MCP 메모리 서버를 붙인다. 도구가 mcp__<서버>__<도구> 형태로 노출되는 걸 확인한다. 배우는 것: 코드 수정 없이 설정 오버레이(--patch)만으로 능력을 얹는 방법, MCP 연결 구조.

LEVEL 4 · 고급 조립 관찰 · 자기수정

--dump-config로 조립 트리 보기 + 자기수정 데모

dsh --profile web --dump-config로 최종 조립된 플러그인 트리(어떤 서비스가 어떤 순서로 켜졌는지)를 관찰한다. 이어 pnpm run demo:cordis를 돌려, 에이전트가 cordis_inspect로 자기 런타임을 보고 cordis_define/cordis_run으로 새 플러그인을 즉석에서 장착하는 과정을 지켜본다. 배우는 것: 부팅 조립의 실체, 자기 참조 도구의 동작.

LEVEL 5 · 도전 플러그인 작성

나만의 Cordis 플러그인 만들기 (도구 또는 모델 어댑터)

seam 3역할을 따라 직접 만든다. (A) ctx.tools에 새 도구를 등록하는 플러그인 — 예: 현재 시간을 반환하는 get_time 도구를 apply(ctx)에서 ctx.effect()로 등록하고 cordis.yml에 한 줄 추가해 켠다. (B) 더 도전적으로는 ctx.llm에 다른 모델 제공자 어댑터를 붙여 DeepSeek 대신 그 모델로 루프를 돌려 본다. 배우는 것: "패키지=플러그인" 규칙과 되돌릴 수 있는 등록을 직접 손으로 익히기.

9관련 기술 심화 학습 로드맵

"dsh를 '읽고 확장하는 사람'이 되기 위한 6주 코스"
주차주제무엇을 · 왜
1주Cordis 기초Context·Service·inject·apply·effect. docs/cordis-primer.md부터. 프레임워크의 문법이자 세계관.
2주dsh 부팅 구조profile·bundle·cordis.yml·--dump-config·--patch. "설정으로 조립"의 실제 메커니즘.
3주에이전트 루프·이벤트turn/step, 워터폴 vs serial, agent/*·tools/* 이벤트. 미들웨어로 루프를 확장하는 법.
4주능력 이음새(seam)fs·shell·sandbox의 정의/제공자/소비자 3역할. 제공자 교체로 이식·테스트하기.
5주서브에이전트·도구claude-code/codex/dsh-sdk 백엔드, 도구 레지스트리 설계, MCP·LSP 연결. 제품 수준 능력 구성.
6주자기수정·샌드박스cordis_* 자기 참조 도구 + Landlock/bwrap 샌드박스. "안전하게 자라는 에이전트"의 마무리.
추천 학습 순서
"Cordis 문법"을 먼저, "에이전트 기능"은 나중에

dsh의 코드는 처음 보면 파일이 227개 패키지로 흩어져 압도적이다. 하지만 밑바닥 문법(Context에 서비스를 꽂고, inject로 기다리고, effect로 되돌린다)은 하나뿐이다. 1~2주에 이 문법과 부팅 조립만 확실히 잡으면, 나머지 패키지는 전부 "같은 문법으로 쓰인 변주"로 읽힌다. 반대로 이 토대를 건너뛰고 에이전트 기능부터 파면 코드가 미로처럼 느껴진다. 프레임워크를 먼저, 기능을 나중에가 이 레포에선 특히 중요하다.

10핵심 키워드 사전

dsh 문서·코드에서 반복되는 용어들
용어
Agent Harness (에이전트 하네스)
LLM을 실제로 일하는 에이전트로 만드는 런타임 층. 모델 호출·도구 실행·대화/세션 관리·샌드박스 등 "모델 주변의 배선 전부"를 가리킨다. dsh는 그 하네스를 플러그인 집합으로 구현했다.
용어
Cordis
dsh의 뼈대가 되는 플러그인/서비스 프레임워크(v4.0.1). "시공간적 조립성을 위한 프로그래밍 패러다임" 논문에 기반. 플러그인이 서비스·타입 이벤트·되돌릴 수 있는 효과를 공유 컨텍스트에 기여한다.
용어
Context (ctx)
서비스들이 이름으로 꽂히는 저장소이자, 플러그인이 세상과 상호작용하는 유일한 창구. ctx.tools, ctx.llm, ctx.on(...), ctx.effect(...)처럼 쓴다. "메인보드"에 해당.
용어
Service (서비스)
ctx.<키>라는 안정적 이름을 차지하는 능력. 다른 플러그인은 구현을 import하지 않고 이 키로 찾는다. 하나의 서비스 키를 여러 구현이 갈아끼울 수 있다(= seam).
용어
inject / apply
inject는 "이 서비스들이 준비되면 나를 켜라"는 의존성 선언(부팅 순서를 대신함). apply(ctx)는 플러그인이 실제로 하는 일 — 서비스 등록·이벤트 리스너 부착이 여기서 일어난다.
용어
Effect (되돌릴 수 있는 효과)
ctx.effect()/ctx.on()으로 한 모든 등록은 해제 함수(disposer)를 돌려준다. 플러그인을 끄면 설치의 역순으로 자동 정리돼 자원 누수가 없다. Cordis의 "시공간적 조립성"의 핵심.
용어
Seam (능력 이음새)
하나의 능력을 이루는 세 역할 — 서비스 정의(인터페이스)·제공자(구현)·소비자(모델이 쥐는 도구). 예: ctx.fs + dsh-fs-local + dsh-tool-fs. 제공자만 바꿔도 제품 동작이 통째로 바뀐다.
용어
Waterfall 이벤트
리스너가 (...인자, next)를 받아, next()를 부르면 다음 리스너로 넘기고(감싸기 가능), 안 부르면 그 자리에서 결정을 가로채는 이벤트. 미들웨어 체인과 같다. agent/request·tools/execute 등이 이 방식.
용어
profile / bundle / cordis.yml
bundle은 배포 단위(플러그인 설정 행 + 코드), profile은 번들들을 순서대로 쌓은 명명된 조합(web·headless). cordis.yml{id, name, config} 행으로 무엇을 mount할지 적은 중앙 설정 파일.
용어
세션 로그 · "model-visible means logged"
모델에게 보이는 모든 것은 세션 로그로부터 재구성 가능해야 한다는 불변식. 새 입력을 주려면 새 세션 이벤트를 남겨야 하고, 런타임이 이를 검사한다. 재현·감사·리플레이가 여기서 나온다.
용어
Landlock
리눅스 커널의 샌드박스 기능(LSM). 프로세스가 스스로에게 파일 접근 규칙을 걸고 exec하면 그 제한이 자식까지 상속된다. dsh는 이를 C로 부른 landlock-run 런처로 도구 실행을 가둔다(커널 5.13+).
용어
ACP · MCP
ACP(Agent Client Protocol)는 에이전트를 외부에서 자동 조종하는 프로토콜(서브에이전트·자동화에 사용). MCP(Model Context Protocol)는 외부 도구·데이터를 표준 방식으로 붙이는 프로토콜. dsh는 둘 다 클라이언트로 지원한다.

11참고 링크

원본을 직접 확인하고 싶을 때