agent-hitch 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,21 +1,30 @@
1
1
  # Hitch
2
2
 
3
- **One local runtime. Any coding agent.**
3
+ [![npm version](https://img.shields.io/npm/v/agent-hitch.svg)](https://www.npmjs.com/package/agent-hitch)
4
+ [![GitHub release](https://img.shields.io/github/v/release/rsi-gear/agent-hitch)](https://github.com/rsi-gear/agent-hitch/releases)
5
+ [![Discord](https://img.shields.io/badge/Discord-Join_chat-5865F2?logo=discord&logoColor=white)](https://discord.gg/cZ4NBbHDk)
4
6
 
5
- Hitch is an agent-first CLI and daemon for discovering and running native coding
6
- agents through one stable, machine-oriented interface.
7
+ **Let agents choose their harness with one runtime.**
7
8
 
8
- It lets an agent choose which harness and revision to use for each run. Hitch is
9
- designed as infrastructure for Recursive Self-Improvement (RSI), including harness evolution and model
10
- evolution.
9
+ Hitch lets an agent choose which harness and revision to use for each run. It is
10
+ an agent-first CLI and daemon for discovering and running native coding agents
11
+ through one stable, machine-oriented interface.
12
+
13
+ Hitch is designed as infrastructure for Recursive Self-Improvement (RSI),
14
+ including harness evolution and model evolution.
11
15
 
12
16
  > Status: pre-alpha. Installed-harness discovery, immutable revision resolution,
13
17
  > prepared artifact caching, direct runs, the local daemon, and Harbor-backed
14
18
  > agent evals are implemented.
15
19
 
20
+ ## News
21
+
22
+ - **2026-08-13:** Hitch now supports DeepSeek Harness.
23
+
16
24
  ## Available now
17
25
 
18
- Hitch currently supports Codex CLI, Claude Code, Pi, and OpenCode adapters. It provides:
26
+ Hitch currently supports Codex CLI, Claude Code, Pi, OpenCode, and DeepSeek
27
+ Harness adapters. It provides:
19
28
 
20
29
  - executable discovery, version probing, and executable fingerprints;
21
30
  - exact package-version and Git-commit resolution;
@@ -98,8 +107,8 @@ hitch daemon cancel run_<id>
98
107
 
99
108
  State is stored below `~/.hitch` by default. Use `--root <path>` or
100
109
  `HITCH_ROOT` to relocate it. Native executable overrides use
101
- `HITCH_CODEX_PATH`, `HITCH_CLAUDE_PATH`, `HITCH_PI_PATH`, and
102
- `HITCH_OPENCODE_PATH`.
110
+ `HITCH_CODEX_PATH`, `HITCH_CLAUDE_PATH`, `HITCH_PI_PATH`,
111
+ `HITCH_OPENCODE_PATH`, and `HITCH_DEEPSEEK_PATH`.
103
112
 
104
113
  ## Harbor-backed evals
105
114
 
@@ -168,8 +177,9 @@ hitch run \
168
177
 
169
178
  Version selectors require exact semantic versions; ranges and `latest` are not
170
179
  accepted. Short commit IDs are expanded and must be unambiguous. Local Git
171
- repositories must be clean. Codex and Pi support source-commit preparation;
172
- Claude Code and OpenCode currently support installed and exact-version sources.
180
+ repositories must be clean. Codex, Pi, and DeepSeek Harness support
181
+ source-commit preparation; Claude Code and OpenCode currently support installed
182
+ and exact-version sources.
173
183
 
174
184
  The legacy `--agent <name>` option remains available as an alias for
175
185
  `--harness <name>@installed`. It cannot select revisions or be combined with
@@ -191,6 +201,7 @@ caller -> Hitch CLI / daemon -> shared run engine -> Codex CLI
191
201
  \----> Claude Code
192
202
  \----> Pi
193
203
  \----> OpenCode
204
+ \----> DeepSeek Harness
194
205
  ```
195
206
 
196
207
  The direct CLI and daemon use the same engine, so persistence, timeout,
@@ -219,6 +230,12 @@ cancellation, and event behavior do not drift.
219
230
  - [Agent daemon analysis and port](docs/daemon.md)
220
231
  - [Workspace isolation](docs/workspaces.md)
221
232
  - [Harbor-backed evals](docs/evals.md)
233
+ - [Release process](docs/releasing.md)
234
+
235
+ ## Community
236
+
237
+ Join the [Hitch community on Discord](https://discord.gg/cZ4NBbHDk) to ask
238
+ questions, share feedback, and discuss coding-agent infrastructure.
222
239
 
223
240
  ## Acknowledgements
224
241
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-hitch",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A local runtime and daemon for coding agents",
5
5
  "keywords": [
6
6
  "ai-agents",
package/src/adapters.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
1
2
  import path from "node:path";
2
3
  import { HitchError } from "./errors.js";
3
4
 
@@ -274,6 +275,54 @@ const definitions = {
274
275
  return translated;
275
276
  },
276
277
  },
278
+ deepseek: {
279
+ id: "deepseek",
280
+ display_name: "DeepSeek Harness",
281
+ command: "dsh",
282
+ path_env: "HITCH_DEEPSEEK_PATH",
283
+ version_args: ["--version"],
284
+ revision_sources: {
285
+ version: { type: "npm", package: "@deepseek-ai/dsh", bin: "dsh" },
286
+ commit: {
287
+ type: "git",
288
+ url: "https://github.com/deepseek-ai/deepseek-harness.git",
289
+ commands: [
290
+ { executable: "pnpm", args: ["install", "--frozen-lockfile"] },
291
+ { executable: "pnpm", args: ["run", "build"] },
292
+ ],
293
+ entrypoint: "apps/cli/lib/bin.js",
294
+ },
295
+ },
296
+ capabilities: {
297
+ non_interactive: true,
298
+ streaming: false,
299
+ structured_messages: false,
300
+ structured_tool_events: false,
301
+ sessions: false,
302
+ resume: false,
303
+ model_selection: true,
304
+ graceful_cancel: true,
305
+ },
306
+ async process(request, executable, runtime = {}) {
307
+ const args = ["--profile", "headless", ...request.agent_args];
308
+ if (request.model) {
309
+ const patchFile = await writeDeepseekModelPatch(request.model, runtime.run_directory);
310
+ args.push("--patch", patchFile);
311
+ }
312
+ args.push(request.prompt);
313
+ return {
314
+ executable,
315
+ args,
316
+ input: "",
317
+ env: runtime.runtime_home ? { DSH_HOME: runtime.runtime_home } : {},
318
+ };
319
+ },
320
+ translateLine(line, state = {}) {
321
+ const text = state.has_stdout_line ? `\n${line}` : line;
322
+ state.has_stdout_line = true;
323
+ return [{ type: "message.delta", text }];
324
+ },
325
+ },
277
326
  };
278
327
 
279
328
  function codexSupportsEphemeral(observedVersion) {
@@ -313,6 +362,31 @@ function openCodeErrorMessage(error) {
313
362
  return error == null ? "OpenCode error" : JSON.stringify(error);
314
363
  }
315
364
 
365
+ async function writeDeepseekModelPatch(value, runDirectory) {
366
+ if (!runDirectory) {
367
+ throw new HitchError("DeepSeek model selection requires an isolated run directory", {
368
+ code: "adapter_setup_failed",
369
+ exitCode: 6,
370
+ });
371
+ }
372
+ const separator = value.indexOf("/");
373
+ const provider = separator < 0 ? "deepseek-official" : value.slice(0, separator);
374
+ const model = separator < 0 ? value : value.slice(separator + 1);
375
+ if (!provider || !model) {
376
+ throw new HitchError(`invalid DeepSeek model selector: ${value}`, {
377
+ code: "invalid_input",
378
+ exitCode: 2,
379
+ });
380
+ }
381
+ const file = path.join(runDirectory, "config", "deepseek-model.json");
382
+ await mkdir(path.dirname(file), { recursive: true });
383
+ await writeFile(file, `${JSON.stringify([{
384
+ id: "agent-default-model",
385
+ config: { provider, model },
386
+ }], null, 2)}\n`, { mode: 0o600 });
387
+ return file;
388
+ }
389
+
316
390
  export function getAdapter(id) {
317
391
  const adapter = definitions[id];
318
392
  if (!adapter) {
package/src/artifacts.js CHANGED
@@ -703,6 +703,7 @@ function commandExecutable(command, env) {
703
703
  git: "HITCH_GIT_PATH",
704
704
  cargo: "HITCH_CARGO_PATH",
705
705
  bun: "HITCH_BUN_PATH",
706
+ pnpm: "HITCH_PNPM_PATH",
706
707
  }[command];
707
708
  return override && env[override]?.trim() ? env[override].trim() : command;
708
709
  }
@@ -849,7 +850,7 @@ async function assertNpmResolution(directory, resolved) {
849
850
  }
850
851
 
851
852
  async function sourceLockIdentity(directory) {
852
- const candidates = ["package-lock.json", "npm-shrinkwrap.json", "bun.lock", path.join("codex-rs", "Cargo.lock")];
853
+ const candidates = ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "bun.lock", path.join("codex-rs", "Cargo.lock")];
853
854
  const locks = {};
854
855
  for (const candidate of candidates) {
855
856
  const identity = await optionalFileDigest(path.join(directory, candidate));
package/src/engine.js CHANGED
@@ -190,9 +190,11 @@ export async function executeRun({ runId, request, runsRoot, root = path.dirname
190
190
  const adapter = getAdapter(reference.harness_id);
191
191
  const adapterState = {};
192
192
  const executionRequest = { ...normalized, cwd: workspaceLease.execution_workspace };
193
- const specification = adapter.process(executionRequest, artifact.executable, {
193
+ const specification = await adapter.process(executionRequest, artifact.executable, {
194
194
  observed_version: artifact.observed_version,
195
195
  resolution,
196
+ run_directory: runDirectory,
197
+ runtime_home: path.join(runDirectory, "runtime-home"),
196
198
  });
197
199
  if (artifact.entrypoint_args?.length) specification.args.unshift(...artifact.entrypoint_args);
198
200
  if (signal?.aborted) {
@@ -201,7 +203,11 @@ export async function executeRun({ runId, request, runsRoot, root = path.dirname
201
203
  } else {
202
204
  stage = "launch";
203
205
  workspaceLease = await markWorkspaceRunning(workspaceLease, { recordPath: workspacePath });
204
- const childEnvironment = { ...process.env, PWD: workspaceLease.execution_workspace };
206
+ const childEnvironment = {
207
+ ...process.env,
208
+ ...specification.env,
209
+ PWD: workspaceLease.execution_workspace,
210
+ };
205
211
  delete childEnvironment.OLDPWD;
206
212
  child = spawn(specification.executable, specification.args, {
207
213
  cwd: workspaceLease.execution_workspace,
@@ -220,6 +226,14 @@ export async function executeRun({ runId, request, runsRoot, root = path.dirname
220
226
 
221
227
  consumeLines(child.stdout, (line) => {
222
228
  sink.writeStdout(line);
229
+ if (adapter.translateLine) {
230
+ for (const event of adapter.translateLine(line, adapterState)) {
231
+ if (event.type === "message.delta" && event.text) messageParts.push(event.text);
232
+ if (event.type === "message.completed" && typeof event.text === "string") finalMessage = event.text;
233
+ sink.emit(event);
234
+ }
235
+ return;
236
+ }
223
237
  let native;
224
238
  try {
225
239
  native = JSON.parse(line);
package/src/eval-tools.js CHANGED
@@ -10,6 +10,8 @@ import { detectVersion, resolveExecutable } from "./registry.js";
10
10
 
11
11
  export const DEFAULT_HARBOR_VERSION = "0.21.0";
12
12
  export const HARBOR_CREDENTIAL_ENV = [
13
+ "DEEPSEEK_API_KEY",
14
+ "DEEPSEEK_BASE_URL",
13
15
  "OPENAI_API_KEY",
14
16
  "OPENAI_BASE_URL",
15
17
  "OPENAI_ORG_ID",
@@ -32,6 +34,7 @@ export const HARBOR_CREDENTIAL_ENV = [
32
34
  "GH_TOKEN",
33
35
  ];
34
36
  const PROVIDER_CREDENTIAL_ENV = [
37
+ "DEEPSEEK_API_KEY",
35
38
  "OPENAI_API_KEY",
36
39
  "ANTHROPIC_API_KEY",
37
40
  "ANTHROPIC_AUTH_TOKEN",