-
Notifications
You must be signed in to change notification settings - Fork 7
feat(dsh-box): DeepSeek Harness provider backed by Upstash Box #228
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
Merged
Changes from 5 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
398f608
feat(dsh-box): DeepSeek Harness provider backed by Upstash Box
alitariksahin 929fa17
fix(dsh-box): review fixes from #228
alitariksahin f75f167
feat(dsh-box): terminal sessions, inherit output, and the 0.1.1-rc.2 …
alitariksahin 41485cc
fix(dsh-box): terminal transport failures, teardown gating, and signa…
alitariksahin a97365c
fix(dsh-box): signal delivery, allocation cancellation, and timer bounds
alitariksahin 4509178
fix(dsh-box): stdin backpressure, cancel ordering, and transport fail…
alitariksahin b31efb9
test(dsh-box): honour the stream error contract for failed stdin writes
alitariksahin a4ec844
fix(dsh-box): settle every teardown attempt before reporting a failure
alitariksahin c0610b6
fix(dsh-box): stop inferring signals, and close two teardown races
alitariksahin b2c4d8f
fix(dsh-box): map dsh-box in publish workflow, cancel stalled termina…
alitariksahin 4e8917a
fix(dsh-box): cancel stalled process setups, drop AbortSignal.any for…
alitariksahin 7c15e37
fix(dsh-box): release stdin on abandon, bound unread pipe and termina…
alitariksahin a5324ae
fix(dsh-box): keep settlement tied to session.wait(), bound inherit o…
alitariksahin 7cf1dd8
test(dsh-box): observe the abandoned session's exit, drop a stale com…
alitariksahin d065397
feat(dsh-box): diagnose a session that fails on a host-side working d…
alitariksahin 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| --- | ||
| "@upstash/dsh-box": minor | ||
| --- | ||
|
|
||
| Add `@upstash/dsh-box`, a DeepSeek Harness provider that runs the harness's | ||
| subprocess seam inside a remote Upstash Box. The harness stays local; only the | ||
| process world moves, so `dsh-bash-local` and anything else built on | ||
| `ctx.subprocess` execute in the box without a fork. | ||
|
|
||
| The package ships two plugins behind one bundle: `@upstash/dsh-box` owns the box | ||
| lifecycle as `ctx.box`, and `@upstash/dsh-box/subprocess` implements the seam as | ||
| `ctx.subprocess`. `dsh plugin add @upstash/dsh-box` mounts both, disabling the | ||
| local provider first so a profile does not boot with two implementations of the | ||
| same service. | ||
|
|
||
| Covers `spawn`, `resolveExecutable`, bounded collect readers, tree-scoped | ||
| termination, `inherit` output, and terminal sessions on a real PTY sized at | ||
| creation. `inspectForeground()` resolves the foreground process group and | ||
| `signalForeground()` delivers to that group rather than the session leader, so | ||
| interrupting a running command leaves the shell alive. Spill files and a | ||
| filesystem adapter are not implemented yet. | ||
|
|
||
| Environment entries cross into the box only when a spawn asks for them | ||
| explicitly, so host ambient values never reach a remote process implicitly, and | ||
| a removal unsets the name in the child rather than blanking it. | ||
|
|
||
| Requires the DeepSeek Harness seam at `0.1.1-rc.2`. |
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,2 @@ | ||
| dist | ||
| examples |
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,11 @@ | ||
| { | ||
| "semi": true, | ||
| "singleQuote": false, | ||
| "trailingComma": "all", | ||
| "printWidth": 100, | ||
| "tabWidth": 2, | ||
| "useTabs": false, | ||
| "bracketSpacing": true, | ||
| "arrowParens": "always", | ||
| "endOfLine": "lf" | ||
| } |
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,82 @@ | ||
| # @upstash/dsh-box | ||
|
|
||
| Run [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) shell work inside a remote [Upstash Box](https://upstash.com/docs/box). | ||
|
|
||
| The harness keeps running locally: your agent, model calls, session state, and tools stay on your machine. Only the process world moves. Bash, and anything else built on the subprocess seam, executes in the box. | ||
|
|
||
| ## Install | ||
|
|
||
| ```bash | ||
| dsh plugin --profile <name> add @upstash/dsh-box | ||
| ``` | ||
|
|
||
| That installs the package and appends its bundle, which mounts two plugins: | ||
|
|
||
| | Plugin | `ctx` key | Role | | ||
| | ----------------------------- | ---------------- | --------------------------------------------------------------------------- | | ||
| | `@upstash/dsh-box` | `ctx.box` | Creates one box, prepares its working directory, and deletes it on disposal | | ||
| | `@upstash/dsh-box/subprocess` | `ctx.subprocess` | Implements the subprocess seam over Box live exec sessions | | ||
|
|
||
| Set `UPSTASH_BOX_API_KEY` in your environment, or pass `apiKey` in the profile. | ||
|
|
||
| Nothing above the seam is forked. `dsh-bash-local` delegates every execution-world operation to `ctx.subprocess`, so mounting the adapter is the whole integration. | ||
|
|
||
| ## Configure | ||
|
|
||
| The bundle's defaults work as-is. To override, edit the rows in your profile: | ||
|
|
||
| ```yaml | ||
| - id: box | ||
| name: "@upstash/dsh-box" | ||
| config: | ||
| cwd: /workspace/home | ||
| runtime: node | ||
|
|
||
| - id: subprocess-box | ||
| name: "@upstash/dsh-box/subprocess" | ||
| ``` | ||
|
|
||
| | Key | Default | Meaning | | ||
| | ------------------ | -------------------------------------------- | ------------------------------------------------------- | | ||
| | `apiKey` | `UPSTASH_BOX_API_KEY` | Account credential. It is never forwarded into the box. | | ||
| | `baseUrl` | `UPSTASH_BOX_BASE_URL`, then the SDK default | API endpoint. | | ||
| | `cwd` | `/workspace/home` | Shared remote working directory. | | ||
| | `runtime` | `node` | Box base image. | | ||
| | `requestTimeoutMs` | `600000` | Per-request HTTP timeout. Not a box lifetime. | | ||
|
|
||
| If you also set a sandbox policy, point its `workspaceRoot` at the same `cwd`, since that is bash's default working directory too. | ||
|
|
||
| ## How it behaves | ||
|
|
||
| - **One box per profile.** The owner creates it at load and deletes it at disposal, so a box does not outlive the fiber that owns it. | ||
| - **The environment does not leak.** Only the environment entries a spawn explicitly asks for cross into the box. Your | ||
| machine's `PATH`, `HOME`, `USER`, `SSH_AUTH_SOCK`, and CI variables stay on your machine. Asking to remove a name | ||
| unsets it in the child rather than blanking it, and the box's own blocked names (`PATH`, `HOME`, `LD_PRELOAD`, | ||
| `LD_LIBRARY_PATH`, `NODE_OPTIONS`) stay under the server's control. | ||
| - **Termination is tree-scoped.** Stopping a command sends SIGTERM to the whole process tree, then SIGKILL after the grace period, so background children cannot outlive the command that started them. | ||
| - **The session owns the process.** Losing the connection stops the command rather than orphaning it in the box. | ||
| - **Terminals get a real PTY.** `spawnTerminal()` allocates one sized by `rows`/`cols` at creation, so a shell reports the | ||
| right size on its first read. Signals go to the foreground process group, so interrupting a running command leaves the | ||
| shell alive. | ||
|
|
||
| ## Requirements | ||
|
|
||
| DeepSeek Harness supplies `@deepseek-ai/cordis`, `@deepseek-ai/dsh-subprocess`, and `@deepseek-ai/dsh-timeout` as peer dependencies. A dsh profile already has them. | ||
|
|
||
| ## Limitations | ||
|
|
||
| - **No filesystem adapter.** Only the process world moves, so `ctx.fs` still resolves against your local machine. Use bash for anything that touches the workspace. | ||
| - **`inherit` copies rather than inherits.** A remote process has no descriptor to hand over, so its bytes are written to | ||
| the harness's own stdout/stderr. Output lands where a local `inherit` would put it, but the child cannot detect a TTY | ||
| through it. | ||
| - **Terminal stdin-wait is best effort.** `inspectForeground()` reads the foreground group from `/proc`, and proves an | ||
| input wait only when the kernel parks a member in a tty read. `inputWaiting` is therefore `false` when the substrate | ||
| offers no evidence, not only when the group is busy. | ||
| - **Collect output has no spill file.** Overflowing output keeps a bounded tail and reports truncation without a recovery path. | ||
| - **Sessions cannot be reattached.** A dropped connection ends the command, so this is the wrong tool for work that must outlive the harness. | ||
| - **`pid` is `-1` briefly.** `spawn()` returns before the session handshake finishes, so a consumer needing a positive pid immediately cannot use this provider unchanged. | ||
| - **The seam is pre-1.0.** It is pinned to `0.1.1-rc.2`; expect breaking changes as DeepSeek Harness evolves. | ||
|
|
||
| ## License | ||
|
|
||
| MIT |
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,26 @@ | ||
| # The @upstash/dsh-box bundle: relocate the execution world into one Upstash Box. | ||
| # | ||
| # Applied over a profile that already carries dsh-base. The local subprocess | ||
| # provider is disabled by id first: cordis allows one implementation per service, | ||
| # so leaving it enabled would make two providers race to register ctx.subprocess | ||
| # and the profile would fail to boot. | ||
| # | ||
| # Bash and every other consumer is untouched. They resolve execution-world | ||
| # operations through ctx.subprocess, so swapping the provider is the whole | ||
| # integration. | ||
| # | ||
| # One-world note: box.cwd and any sandbox-policy workspaceRoot must name the | ||
| # same remote directory, which is also bash-local's default workdir. | ||
|
|
||
| - id: subprocess | ||
| name: "@deepseek-ai/dsh-subprocess-local" | ||
| disabled: true | ||
|
|
||
| - insert: | ||
| - id: box | ||
| name: "@upstash/dsh-box" | ||
| config: | ||
| cwd: /workspace/home | ||
|
|
||
| - id: subprocess-box | ||
| name: "@upstash/dsh-box/subprocess" |
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,79 @@ | ||
| { | ||
| "name": "@upstash/dsh-box", | ||
| "version": "0.0.0", | ||
| "description": "DeepSeek Harness provider that runs the subprocess seam inside a remote Upstash Box", | ||
| "type": "module", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/upstash/box.git", | ||
| "directory": "packages/dsh-box" | ||
| }, | ||
| "author": "Upstash, Inc.", | ||
| "license": "MIT", | ||
| "bugs": { | ||
| "url": "https://github.com/upstash/box/issues" | ||
| }, | ||
| "homepage": "https://github.com/upstash/box/tree/main/packages/dsh-box#readme", | ||
| "keywords": [ | ||
| "deepseek-harness", | ||
| "dsh", | ||
| "dsh-plugin", | ||
| "cordis", | ||
| "upstash", | ||
| "box", | ||
| "sandbox" | ||
| ], | ||
| "dsh": { | ||
| "bundle": { | ||
| "patch": "./cordis.patch.yml" | ||
| } | ||
| }, | ||
| "publishConfig": { | ||
| "access": "public" | ||
| }, | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.js" | ||
| }, | ||
| "./subprocess": { | ||
| "types": "./dist/subprocess.d.ts", | ||
| "import": "./dist/subprocess.js" | ||
| }, | ||
| "./package.json": "./package.json" | ||
| }, | ||
| "files": [ | ||
| "dist", | ||
| "cordis.patch.yml", | ||
| "README.md" | ||
| ], | ||
| "scripts": { | ||
| "build": "tsc", | ||
| "dev": "tsc --watch", | ||
| "typecheck": "tsc --noEmit", | ||
| "test": "vitest run", | ||
| "format": "prettier --write .", | ||
| "ci:lint": "prettier --check .", | ||
| "test:integration": "vitest run -c vitest.integration.config.ts" | ||
| }, | ||
| "dependencies": { | ||
| "@upstash/box": "workspace:*", | ||
| "@deepseek-ai/schemastery": "^3.18.1" | ||
| }, | ||
| "peerDependencies": { | ||
| "@deepseek-ai/cordis": "^4.0.1", | ||
| "@deepseek-ai/dsh-subprocess": "^0.1.1-rc.2", | ||
| "@deepseek-ai/dsh-timeout": "^0.1.1-rc.2" | ||
|
alitariksahin marked this conversation as resolved.
|
||
| }, | ||
| "devDependencies": { | ||
| "@deepseek-ai/cordis": "^4.0.1", | ||
| "@deepseek-ai/dsh-subprocess": "^0.1.1-rc.2", | ||
| "@deepseek-ai/dsh-timeout": "^0.1.1-rc.2", | ||
| "@types/node": "^20.10.0", | ||
| "prettier": "^3.8.1", | ||
| "typescript": "^5.3.0", | ||
| "vitest": "^4.1.2" | ||
| } | ||
| } | ||
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,26 @@ | ||
| /** | ||
| * A promise with its settlement functions exposed. | ||
| * | ||
| * `Promise.withResolvers` would say this in one line, but it landed in Node 22 | ||
| * and this package supports Node 18, so the handle would throw on construction | ||
| * for two supported major versions. | ||
| */ | ||
| export interface Deferred<T> { | ||
| readonly promise: Promise<T>; | ||
| resolve(value: T): void; | ||
| reject(reason: unknown): void; | ||
| } | ||
|
|
||
| /** | ||
| * Create a deferred promise. | ||
| * @returns the promise and its settlement functions. | ||
| */ | ||
| export function deferred<T>(): Deferred<T> { | ||
| let resolve!: (value: T) => void; | ||
| let reject!: (reason: unknown) => void; | ||
| const promise = new Promise<T>((res, rej) => { | ||
| resolve = res; | ||
| reject = rej; | ||
| }); | ||
| return { promise, resolve, reject }; | ||
| } |
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.