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 +28 -11
- package/package.json +1 -1
- package/src/adapters.js +74 -0
- package/src/artifacts.js +2 -1
- package/src/engine.js +16 -2
- package/src/eval-tools.js +3 -0
package/README.md
CHANGED
|
@@ -1,21 +1,30 @@
|
|
|
1
1
|
# Hitch
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/agent-hitch)
|
|
4
|
+
[](https://github.com/rsi-gear/agent-hitch/releases)
|
|
5
|
+
[](https://discord.gg/cZ4NBbHDk)
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
agents through one stable, machine-oriented interface.
|
|
7
|
+
**Let agents choose their harness with one runtime.**
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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,
|
|
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`,
|
|
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
|
|
172
|
-
Claude Code and OpenCode currently support installed
|
|
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
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 = {
|
|
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",
|