agent-hitch 0.1.1 → 0.2.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.
Files changed (263) hide show
  1. package/README.md +235 -132
  2. package/README.zh-CN.md +335 -0
  3. package/dist/bin/hitch.js +9 -0
  4. package/dist/bin/hitch.js.map +1 -0
  5. package/dist/scripts/check-architecture.js +159 -0
  6. package/dist/scripts/check-architecture.js.map +1 -0
  7. package/dist/scripts/check-release.js +25 -0
  8. package/dist/scripts/check-release.js.map +1 -0
  9. package/dist/scripts/check-syntax.js +32 -0
  10. package/dist/scripts/check-syntax.js.map +1 -0
  11. package/dist/src/adapters/catalog.js +55 -0
  12. package/dist/src/adapters/catalog.js.map +1 -0
  13. package/dist/src/adapters/contract.js +2 -0
  14. package/dist/src/adapters/contract.js.map +1 -0
  15. package/dist/src/adapters/discovery.js +22 -0
  16. package/dist/src/adapters/discovery.js.map +1 -0
  17. package/dist/src/adapters/index.js +3 -0
  18. package/dist/src/adapters/index.js.map +1 -0
  19. package/dist/src/adapters/providers/claude.js +69 -0
  20. package/dist/src/adapters/providers/claude.js.map +1 -0
  21. package/dist/src/adapters/providers/codex.js +74 -0
  22. package/dist/src/adapters/providers/codex.js.map +1 -0
  23. package/dist/src/adapters/providers/deepseek.js +51 -0
  24. package/dist/src/adapters/providers/deepseek.js.map +1 -0
  25. package/dist/src/adapters/providers/opencode.js +73 -0
  26. package/dist/src/adapters/providers/opencode.js.map +1 -0
  27. package/dist/src/adapters/providers/pi.js +82 -0
  28. package/dist/src/adapters/providers/pi.js.map +1 -0
  29. package/dist/src/adapters/providers/shared.js +82 -0
  30. package/dist/src/adapters/providers/shared.js.map +1 -0
  31. package/dist/src/artifacts/index.js +3 -0
  32. package/dist/src/artifacts/index.js.map +1 -0
  33. package/dist/src/artifacts/integrity.js +188 -0
  34. package/dist/src/artifacts/integrity.js.map +1 -0
  35. package/dist/src/artifacts/preparer.js +379 -0
  36. package/dist/src/artifacts/preparer.js.map +1 -0
  37. package/dist/src/artifacts/store.js +87 -0
  38. package/dist/src/artifacts/store.js.map +1 -0
  39. package/dist/src/artifacts/types.js +2 -0
  40. package/dist/src/artifacts/types.js.map +1 -0
  41. package/dist/src/backends/contract.js +2 -0
  42. package/dist/src/backends/contract.js.map +1 -0
  43. package/dist/src/backends/harbor/backend.js +305 -0
  44. package/dist/src/backends/harbor/backend.js.map +1 -0
  45. package/dist/src/backends/harbor/index.js +4 -0
  46. package/dist/src/backends/harbor/index.js.map +1 -0
  47. package/dist/src/backends/harbor/local-git-transport.js +447 -0
  48. package/dist/src/backends/harbor/local-git-transport.js.map +1 -0
  49. package/dist/src/backends/harbor/tools.js +314 -0
  50. package/dist/src/backends/harbor/tools.js.map +1 -0
  51. package/dist/src/backends/index.js +4 -0
  52. package/dist/src/backends/index.js.map +1 -0
  53. package/dist/src/cli/arguments.js +123 -0
  54. package/dist/src/cli/arguments.js.map +1 -0
  55. package/dist/src/cli/commands/compare.js +55 -0
  56. package/dist/src/cli/commands/compare.js.map +1 -0
  57. package/dist/src/cli/commands/daemon.js +106 -0
  58. package/dist/src/cli/commands/daemon.js.map +1 -0
  59. package/dist/src/cli/commands/eval.js +126 -0
  60. package/dist/src/cli/commands/eval.js.map +1 -0
  61. package/dist/src/cli/commands/feedback.js +84 -0
  62. package/dist/src/cli/commands/feedback.js.map +1 -0
  63. package/dist/src/cli/commands/inspect.js +20 -0
  64. package/dist/src/cli/commands/inspect.js.map +1 -0
  65. package/dist/src/cli/commands/list.js +17 -0
  66. package/dist/src/cli/commands/list.js.map +1 -0
  67. package/dist/src/cli/commands/prepare.js +21 -0
  68. package/dist/src/cli/commands/prepare.js.map +1 -0
  69. package/dist/src/cli/commands/resolve.js +17 -0
  70. package/dist/src/cli/commands/resolve.js.map +1 -0
  71. package/dist/src/cli/commands/run.js +83 -0
  72. package/dist/src/cli/commands/run.js.map +1 -0
  73. package/dist/src/cli/commands/runs.js +45 -0
  74. package/dist/src/cli/commands/runs.js.map +1 -0
  75. package/dist/src/cli/commands/trajectory.js +31 -0
  76. package/dist/src/cli/commands/trajectory.js.map +1 -0
  77. package/dist/src/cli/commands/workspace.js +44 -0
  78. package/dist/src/cli/commands/workspace.js.map +1 -0
  79. package/dist/src/cli/index.js +2 -0
  80. package/dist/src/cli/index.js.map +1 -0
  81. package/dist/src/cli/main.js +48 -0
  82. package/dist/src/cli/main.js.map +1 -0
  83. package/dist/src/cli/output.js +96 -0
  84. package/dist/src/cli/output.js.map +1 -0
  85. package/dist/src/controller-runtime/hash.js +353 -0
  86. package/dist/src/controller-runtime/hash.js.map +1 -0
  87. package/dist/src/controller-runtime/index.js +3 -0
  88. package/dist/src/controller-runtime/index.js.map +1 -0
  89. package/dist/src/controller-runtime/store.js +318 -0
  90. package/dist/src/controller-runtime/store.js.map +1 -0
  91. package/dist/src/daemon/auth.js +108 -0
  92. package/dist/src/daemon/auth.js.map +1 -0
  93. package/dist/src/daemon/client.js +76 -0
  94. package/dist/src/daemon/client.js.map +1 -0
  95. package/dist/src/daemon/index.js +5 -0
  96. package/dist/src/daemon/index.js.map +1 -0
  97. package/dist/src/daemon/launcher.js +24 -0
  98. package/dist/src/daemon/launcher.js.map +1 -0
  99. package/dist/src/daemon/scheduler.js +161 -0
  100. package/dist/src/daemon/scheduler.js.map +1 -0
  101. package/dist/src/daemon/server.js +316 -0
  102. package/dist/src/daemon/server.js.map +1 -0
  103. package/dist/src/domain/artifacts.js +2 -0
  104. package/dist/src/domain/artifacts.js.map +1 -0
  105. package/dist/src/domain/controller-runtime.js +2 -0
  106. package/dist/src/domain/controller-runtime.js.map +1 -0
  107. package/dist/src/domain/eval-records.js +2 -0
  108. package/dist/src/domain/eval-records.js.map +1 -0
  109. package/dist/src/domain/evals.js +2 -0
  110. package/dist/src/domain/evals.js.map +1 -0
  111. package/dist/src/domain/feedback.js +2 -0
  112. package/dist/src/domain/feedback.js.map +1 -0
  113. package/dist/src/domain/ids.js +10 -0
  114. package/dist/src/domain/ids.js.map +1 -0
  115. package/dist/src/domain/index.js +11 -0
  116. package/dist/src/domain/index.js.map +1 -0
  117. package/dist/src/domain/revisions.js +2 -0
  118. package/dist/src/domain/revisions.js.map +1 -0
  119. package/dist/src/domain/runs.js +3 -0
  120. package/dist/src/domain/runs.js.map +1 -0
  121. package/dist/src/domain/trajectories.js +2 -0
  122. package/dist/src/domain/trajectories.js.map +1 -0
  123. package/dist/src/domain/validation.js +429 -0
  124. package/dist/src/domain/validation.js.map +1 -0
  125. package/dist/src/domain/workspaces.js +2 -0
  126. package/dist/src/domain/workspaces.js.map +1 -0
  127. package/dist/src/evals/events.js +67 -0
  128. package/dist/src/evals/events.js.map +1 -0
  129. package/dist/src/evals/index.js +5 -0
  130. package/dist/src/evals/index.js.map +1 -0
  131. package/dist/src/evals/records.js +55 -0
  132. package/dist/src/evals/records.js.map +1 -0
  133. package/dist/src/evals/request.js +109 -0
  134. package/dist/src/evals/request.js.map +1 -0
  135. package/dist/src/evals/service.js +227 -0
  136. package/dist/src/evals/service.js.map +1 -0
  137. package/dist/src/evals/trial-import.js +394 -0
  138. package/dist/src/evals/trial-import.js.map +1 -0
  139. package/dist/src/feedback/index.js +2 -0
  140. package/dist/src/feedback/index.js.map +1 -0
  141. package/dist/src/feedback/service.js +314 -0
  142. package/dist/src/feedback/service.js.map +1 -0
  143. package/dist/src/foundation/config.js +54 -0
  144. package/dist/src/foundation/config.js.map +1 -0
  145. package/dist/src/foundation/errors.js +14 -0
  146. package/dist/src/foundation/errors.js.map +1 -0
  147. package/dist/src/foundation/executable.js +135 -0
  148. package/dist/src/foundation/executable.js.map +1 -0
  149. package/dist/src/foundation/fs.js +45 -0
  150. package/dist/src/foundation/fs.js.map +1 -0
  151. package/dist/src/foundation/hash.js +24 -0
  152. package/dist/src/foundation/hash.js.map +1 -0
  153. package/dist/src/foundation/index.js +11 -0
  154. package/dist/src/foundation/index.js.map +1 -0
  155. package/dist/src/foundation/line-stream.js +20 -0
  156. package/dist/src/foundation/line-stream.js.map +1 -0
  157. package/dist/src/foundation/locks.js +114 -0
  158. package/dist/src/foundation/locks.js.map +1 -0
  159. package/dist/src/foundation/package-root.js +41 -0
  160. package/dist/src/foundation/package-root.js.map +1 -0
  161. package/dist/src/foundation/process.js +50 -0
  162. package/dist/src/foundation/process.js.map +1 -0
  163. package/dist/src/revisions/index.js +4 -0
  164. package/dist/src/revisions/index.js.map +1 -0
  165. package/dist/src/revisions/reference.js +97 -0
  166. package/dist/src/revisions/reference.js.map +1 -0
  167. package/dist/src/revisions/resolver.js +25 -0
  168. package/dist/src/revisions/resolver.js.map +1 -0
  169. package/dist/src/revisions/sources/git.js +180 -0
  170. package/dist/src/revisions/sources/git.js.map +1 -0
  171. package/dist/src/revisions/sources/installed.js +45 -0
  172. package/dist/src/revisions/sources/installed.js.map +1 -0
  173. package/dist/src/revisions/sources/npm.js +97 -0
  174. package/dist/src/revisions/sources/npm.js.map +1 -0
  175. package/dist/src/runs/compare.js +132 -0
  176. package/dist/src/runs/compare.js.map +1 -0
  177. package/dist/src/runs/events.js +79 -0
  178. package/dist/src/runs/events.js.map +1 -0
  179. package/dist/src/runs/executor.js +480 -0
  180. package/dist/src/runs/executor.js.map +1 -0
  181. package/dist/src/runs/finalizer.js +22 -0
  182. package/dist/src/runs/finalizer.js.map +1 -0
  183. package/dist/src/runs/identity.js +35 -0
  184. package/dist/src/runs/identity.js.map +1 -0
  185. package/dist/src/runs/index.js +11 -0
  186. package/dist/src/runs/index.js.map +1 -0
  187. package/dist/src/runs/manifest.js +70 -0
  188. package/dist/src/runs/manifest.js.map +1 -0
  189. package/dist/src/runs/outcome.js +49 -0
  190. package/dist/src/runs/outcome.js.map +1 -0
  191. package/dist/src/runs/query.js +104 -0
  192. package/dist/src/runs/query.js.map +1 -0
  193. package/dist/src/runs/queued.js +54 -0
  194. package/dist/src/runs/queued.js.map +1 -0
  195. package/dist/src/runs/records.js +288 -0
  196. package/dist/src/runs/records.js.map +1 -0
  197. package/dist/src/runs/request.js +169 -0
  198. package/dist/src/runs/request.js.map +1 -0
  199. package/dist/src/trajectories/contract.js +17 -0
  200. package/dist/src/trajectories/contract.js.map +1 -0
  201. package/dist/src/trajectories/format.js +127 -0
  202. package/dist/src/trajectories/format.js.map +1 -0
  203. package/dist/src/trajectories/index.js +7 -0
  204. package/dist/src/trajectories/index.js.map +1 -0
  205. package/dist/src/trajectories/projector.js +385 -0
  206. package/dist/src/trajectories/projector.js.map +1 -0
  207. package/dist/src/trajectories/provider-capture.js +133 -0
  208. package/dist/src/trajectories/provider-capture.js.map +1 -0
  209. package/dist/src/trajectories/providers/deepseek.js +179 -0
  210. package/dist/src/trajectories/providers/deepseek.js.map +1 -0
  211. package/dist/src/trajectories/store.js +421 -0
  212. package/dist/src/trajectories/store.js.map +1 -0
  213. package/dist/src/workspaces/copy.js +142 -0
  214. package/dist/src/workspaces/copy.js.map +1 -0
  215. package/dist/src/workspaces/digest.js +47 -0
  216. package/dist/src/workspaces/digest.js.map +1 -0
  217. package/dist/src/workspaces/git.js +164 -0
  218. package/dist/src/workspaces/git.js.map +1 -0
  219. package/dist/src/workspaces/index.js +6 -0
  220. package/dist/src/workspaces/index.js.map +1 -0
  221. package/dist/src/workspaces/lifecycle.js +313 -0
  222. package/dist/src/workspaces/lifecycle.js.map +1 -0
  223. package/dist/src/workspaces/planner.js +80 -0
  224. package/dist/src/workspaces/planner.js.map +1 -0
  225. package/dist/src/workspaces/store.js +25 -0
  226. package/dist/src/workspaces/store.js.map +1 -0
  227. package/dist/src/workspaces/types.js +4 -0
  228. package/dist/src/workspaces/types.js.map +1 -0
  229. package/dist/src/workspaces/utils.js +56 -0
  230. package/dist/src/workspaces/utils.js.map +1 -0
  231. package/docs/schemas/controller-runtime-manifest.schema.json +57 -0
  232. package/docs/schemas/controller-runtime-ref.schema.json +16 -0
  233. package/docs/schemas/eval-request.schema.json +2 -0
  234. package/docs/schemas/eval-result.schema.json +36 -1
  235. package/docs/schemas/local-git-transport-manifest.schema.json +34 -0
  236. package/docs/schemas/message-feedback.schema.json +37 -0
  237. package/docs/schemas/result.schema.json +9 -0
  238. package/docs/schemas/run-context.schema.json +39 -0
  239. package/docs/schemas/run-manifest.schema.json +81 -0
  240. package/docs/schemas/run-request.schema.json +16 -1
  241. package/docs/schemas/trajectory-ref.schema.json +73 -0
  242. package/integrations/harbor/hitch_harbor_agent.py +551 -5
  243. package/package.json +37 -11
  244. package/bin/hitch.js +0 -9
  245. package/src/adapters.js +0 -435
  246. package/src/artifacts.js +0 -949
  247. package/src/cli.js +0 -505
  248. package/src/config.js +0 -51
  249. package/src/daemon.js +0 -416
  250. package/src/engine.js +0 -400
  251. package/src/errors.js +0 -12
  252. package/src/eval-tools.js +0 -334
  253. package/src/evals.js +0 -276
  254. package/src/events.js +0 -69
  255. package/src/fs.js +0 -43
  256. package/src/harbor-backend.js +0 -285
  257. package/src/harness-reference.js +0 -78
  258. package/src/line-stream.js +0 -17
  259. package/src/locks.js +0 -33
  260. package/src/process.js +0 -49
  261. package/src/registry.js +0 -84
  262. package/src/scheduler.js +0 -156
  263. package/src/workspaces.js +0 -893
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "agent-hitch",
3
- "version": "0.1.1",
4
- "description": "A local runtime and daemon for coding agents",
3
+ "version": "0.2.1",
4
+ "description": "Content-addressed version control and evidence storage for agent harnesses",
5
5
  "keywords": [
6
6
  "ai-agents",
7
7
  "cli",
8
8
  "coding-agents",
9
- "daemon"
9
+ "daemon",
10
+ "version-control"
10
11
  ],
11
12
  "homepage": "https://github.com/rsi-gear/agent-hitch#readme",
12
13
  "bugs": {
@@ -17,19 +18,40 @@
17
18
  "url": "git+https://github.com/rsi-gear/agent-hitch.git"
18
19
  },
19
20
  "type": "module",
21
+ "exports": {
22
+ "./adapters": "./dist/src/adapters/index.js",
23
+ "./artifacts": "./dist/src/artifacts/index.js",
24
+ "./backends": "./dist/src/backends/index.js",
25
+ "./controller-runtime": "./dist/src/controller-runtime/index.js",
26
+ "./daemon": "./dist/src/daemon/index.js",
27
+ "./domain": "./dist/src/domain/index.js",
28
+ "./evals": "./dist/src/evals/index.js",
29
+ "./feedback": "./dist/src/feedback/index.js",
30
+ "./foundation": "./dist/src/foundation/index.js",
31
+ "./revisions": "./dist/src/revisions/index.js",
32
+ "./runs": "./dist/src/runs/index.js",
33
+ "./trajectories": "./dist/src/trajectories/index.js",
34
+ "./workspaces": "./dist/src/workspaces/index.js",
35
+ "./cli": "./dist/src/cli/index.js",
36
+ "./package.json": "./package.json"
37
+ },
20
38
  "bin": {
21
- "hitch": "bin/hitch.js"
39
+ "hitch": "dist/bin/hitch.js"
22
40
  },
23
41
  "files": [
24
- "bin/",
25
- "src/",
26
- "integrations/harbor/hitch_harbor_agent.py",
27
- "docs/schemas/"
42
+ "dist/bin/",
43
+ "dist/src/",
44
+ "dist/scripts/",
45
+ "docs/schemas/",
46
+ "integrations/harbor/hitch_harbor_agent.py"
28
47
  ],
29
48
  "scripts": {
30
- "test": "node --test",
31
- "coverage": "node --experimental-test-coverage --test-coverage-include=src/*.js --test-coverage-lines=75 --test-coverage-branches=65 --test-coverage-functions=70 --test",
32
- "check": "node scripts/check-syntax.js && node --test",
49
+ "typecheck": "tsc --noEmit",
50
+ "build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json",
51
+ "test": "node --test \"dist/test/*.test.js\"",
52
+ "coverage": "node --experimental-test-coverage --test-coverage-include=dist/src/**/*.js --test-coverage-lines=75 --test-coverage-branches=65 --test-coverage-functions=70 --test \"dist/test/*.test.js\"",
53
+ "check:architecture": "node dist/scripts/check-architecture.js",
54
+ "check": "npm run typecheck && npm run build && npm run check:architecture && node dist/scripts/check-syntax.js && node --test \"dist/test/*.test.js\"",
33
55
  "prepublishOnly": "npm run check"
34
56
  },
35
57
  "engines": {
@@ -38,5 +60,9 @@
38
60
  "publishConfig": {
39
61
  "access": "public"
40
62
  },
63
+ "devDependencies": {
64
+ "@types/node": "^22.20.1",
65
+ "typescript": "^5.9.3"
66
+ },
41
67
  "license": "Apache-2.0"
42
68
  }
package/bin/hitch.js DELETED
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { main } from "../src/cli.js";
4
-
5
- main(process.argv.slice(2)).catch((error) => {
6
- const message = error instanceof Error ? error.message : String(error);
7
- process.stderr.write(`hitch: ${message}\n`);
8
- process.exitCode = Number.isInteger(error?.exitCode) ? error.exitCode : 12;
9
- });
package/src/adapters.js DELETED
@@ -1,435 +0,0 @@
1
- import { mkdir, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { HitchError } from "./errors.js";
4
-
5
- const definitions = {
6
- codex: {
7
- id: "codex",
8
- display_name: "Codex CLI",
9
- command: "codex",
10
- path_env: "HITCH_CODEX_PATH",
11
- version_args: ["--version"],
12
- revision_sources: {
13
- version: { type: "npm", package: "@openai/codex", bin: "codex" },
14
- commit: {
15
- type: "git",
16
- url: "https://github.com/openai/codex.git",
17
- commands: [
18
- { executable: "cargo", args: ["build", "--release", "--locked", "--bin", "codex"], cwd: "codex-rs" },
19
- ],
20
- entrypoint: "codex-rs/target/release/codex",
21
- },
22
- },
23
- capabilities: {
24
- non_interactive: true,
25
- streaming: true,
26
- structured_messages: true,
27
- structured_tool_events: true,
28
- sessions: true,
29
- resume: false,
30
- model_selection: true,
31
- graceful_cancel: true,
32
- },
33
- process(request, executable, runtime = {}) {
34
- const args = ["exec", "--json"];
35
- if (codexSupportsEphemeral(runtime.observed_version)) args.push("--ephemeral");
36
- args.push("--skip-git-repo-check", "--color", "never", "-C", request.cwd);
37
- if (request.model) args.push("--model", request.model);
38
- args.push(...request.agent_args, "-");
39
- return { executable, args, input: request.prompt };
40
- },
41
- translate(event) {
42
- if (event.type === "thread.started") {
43
- return [{ type: "session.created", session_id: event.thread_id }];
44
- }
45
- if (event.type === "item.completed" && event.item?.type === "agent_message") {
46
- return [{ type: "message.delta", text: event.item.text || "" }];
47
- }
48
- const toolTypes = new Set(["command_execution", "file_change", "mcp_tool_call", "web_search"]);
49
- if (event.type === "item.started" && toolTypes.has(event.item?.type)) {
50
- return [{
51
- type: "tool.started",
52
- call_id: event.item.id,
53
- name: event.item.type,
54
- native: event.item,
55
- }];
56
- }
57
- if (event.type === "item.completed" && toolTypes.has(event.item?.type)) {
58
- return [{
59
- type: "tool.completed",
60
- call_id: event.item.id,
61
- name: event.item.type,
62
- status: event.item.status || "completed",
63
- native: event.item,
64
- }];
65
- }
66
- if (event.type === "turn.completed" && event.usage) {
67
- return [{ type: "usage.updated", usage: event.usage }];
68
- }
69
- if (event.type === "error") {
70
- return [{ type: "diagnostic", level: "error", message: event.message || "Codex error" }];
71
- }
72
- return [{ type: "provider.event", provider_type: event.type || "unknown", native: event }];
73
- },
74
- },
75
- claude: {
76
- id: "claude",
77
- display_name: "Claude Code",
78
- command: "claude",
79
- path_env: "HITCH_CLAUDE_PATH",
80
- version_args: ["--version"],
81
- revision_sources: {
82
- version: { type: "npm", package: "@anthropic-ai/claude-code", bin: "claude" },
83
- },
84
- capabilities: {
85
- non_interactive: true,
86
- streaming: true,
87
- structured_messages: true,
88
- structured_tool_events: true,
89
- sessions: true,
90
- resume: false,
91
- model_selection: true,
92
- graceful_cancel: true,
93
- },
94
- process(request, executable) {
95
- const args = ["-p", "--output-format", "stream-json", "--verbose"];
96
- if (request.model) args.push("--model", request.model);
97
- args.push(...request.agent_args);
98
- return { executable, args, input: request.prompt };
99
- },
100
- translate(event) {
101
- if (event.type === "system" && event.session_id) {
102
- return [{ type: "session.created", session_id: event.session_id }];
103
- }
104
- if (event.type === "assistant") {
105
- const content = event.message?.content || [];
106
- return content.flatMap((block) => {
107
- if (block.type === "text") return [{ type: "message.delta", text: block.text || "" }];
108
- if (block.type === "tool_use") {
109
- return [{ type: "tool.started", call_id: block.id, name: block.name, input: block.input }];
110
- }
111
- return [];
112
- });
113
- }
114
- if (event.type === "user") {
115
- const content = event.message?.content || [];
116
- return content.flatMap((block) => {
117
- if (block.type !== "tool_result") return [];
118
- return [{
119
- type: "tool.completed",
120
- call_id: block.tool_use_id,
121
- status: block.is_error ? "failed" : "succeeded",
122
- output: claudeToolResultText(block.content),
123
- }];
124
- });
125
- }
126
- if (event.type === "result") {
127
- const translated = [];
128
- if (typeof event.result === "string") translated.push({ type: "message.completed", text: event.result });
129
- if (event.usage) translated.push({ type: "usage.updated", usage: event.usage });
130
- return translated;
131
- }
132
- return [{ type: "provider.event", provider_type: event.type || "unknown", native: event }];
133
- },
134
- },
135
- pi: {
136
- id: "pi",
137
- display_name: "Pi Coding Agent",
138
- command: "pi",
139
- path_env: "HITCH_PI_PATH",
140
- version_args: ["--version"],
141
- revision_sources: {
142
- version: {
143
- type: "npm",
144
- packages: ["@earendil-works/pi-coding-agent", "@mariozechner/pi-coding-agent"],
145
- bin: "pi",
146
- },
147
- commit: {
148
- type: "git",
149
- url: "https://github.com/earendil-works/pi.git",
150
- commands: [
151
- { executable: "npm", args: ["ci", "--ignore-scripts"] },
152
- { executable: "npm", args: ["run", "build"] },
153
- ],
154
- entrypoint: "packages/coding-agent/dist/cli.js",
155
- },
156
- },
157
- capabilities: {
158
- non_interactive: true,
159
- streaming: true,
160
- structured_messages: true,
161
- structured_tool_events: true,
162
- sessions: true,
163
- resume: false,
164
- model_selection: true,
165
- graceful_cancel: true,
166
- },
167
- process(request, executable) {
168
- const args = ["--mode", "json", "--no-session"];
169
- if (request.model) args.push("--model", request.model);
170
- args.push(...request.agent_args);
171
- return { executable, args, input: request.prompt };
172
- },
173
- translate(event) {
174
- if (event.type === "session" && event.id) {
175
- return [{ type: "session.created", session_id: event.id }];
176
- }
177
- if (event.type === "message_update" && event.assistantMessageEvent?.type === "text_delta") {
178
- return [{ type: "message.delta", text: event.assistantMessageEvent.delta || "" }];
179
- }
180
- if (event.type === "message_end" && event.message?.role === "assistant") {
181
- const translated = [{ type: "message.completed", text: assistantMessageText(event.message) }];
182
- if (event.message.usage) translated.push({ type: "usage.updated", usage: event.message.usage });
183
- if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
184
- translated.push({
185
- type: "diagnostic",
186
- level: "error",
187
- message: event.message.errorMessage || `Pi request ${event.message.stopReason}`,
188
- });
189
- }
190
- return translated;
191
- }
192
- if (event.type === "tool_execution_start") {
193
- return [{
194
- type: "tool.started",
195
- call_id: event.toolCallId,
196
- name: event.toolName,
197
- input: event.args,
198
- }];
199
- }
200
- if (event.type === "tool_execution_end") {
201
- return [{
202
- type: "tool.completed",
203
- call_id: event.toolCallId,
204
- name: event.toolName,
205
- status: event.isError ? "failed" : "succeeded",
206
- output: event.result,
207
- }];
208
- }
209
- return [{ type: "provider.event", provider_type: event.type || "unknown", native: event }];
210
- },
211
- },
212
- opencode: {
213
- id: "opencode",
214
- display_name: "OpenCode",
215
- command: "opencode",
216
- path_env: "HITCH_OPENCODE_PATH",
217
- version_args: ["--version"],
218
- revision_sources: {
219
- version: { type: "npm", package: "opencode-ai", bin: "opencode" },
220
- },
221
- capabilities: {
222
- non_interactive: true,
223
- streaming: true,
224
- structured_messages: true,
225
- structured_tool_events: true,
226
- sessions: true,
227
- resume: false,
228
- model_selection: true,
229
- graceful_cancel: true,
230
- },
231
- process(request, executable) {
232
- const args = ["run", "--format", "json", "--dir", request.cwd];
233
- if (request.model) args.push("--model", request.model);
234
- args.push(...request.agent_args);
235
- return { executable, args, input: request.prompt };
236
- },
237
- translate(event, state = {}) {
238
- const translated = [];
239
- if (event.sessionID && state.session_id !== event.sessionID) {
240
- state.session_id = event.sessionID;
241
- translated.push({ type: "session.created", session_id: event.sessionID });
242
- }
243
- if (event.type === "text") {
244
- translated.push({
245
- type: "message.delta",
246
- text: typeof event.part?.text === "string" ? event.part.text : event.text || "",
247
- });
248
- return translated;
249
- }
250
- if (event.type === "tool_use" && event.part) {
251
- const failed = event.part.state?.status === "error";
252
- translated.push({
253
- type: "tool.completed",
254
- call_id: event.part.callID || event.part.id,
255
- name: event.part.tool,
256
- status: failed ? "failed" : "succeeded",
257
- input: event.part.state?.input,
258
- output: failed ? event.part.state?.error : event.part.state?.output,
259
- native: event.part,
260
- });
261
- return translated;
262
- }
263
- if (event.type === "step_finish" && event.part?.tokens) {
264
- translated.push({
265
- type: "usage.updated",
266
- usage: { ...event.part.tokens, cost: event.part.cost },
267
- });
268
- return translated;
269
- }
270
- if (event.type === "error") {
271
- translated.push({ type: "diagnostic", level: "error", message: openCodeErrorMessage(event.error) });
272
- return translated;
273
- }
274
- translated.push({ type: "provider.event", provider_type: event.type || "unknown", native: event });
275
- return translated;
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
- },
326
- };
327
-
328
- function codexSupportsEphemeral(observedVersion) {
329
- const match = String(observedVersion || "").match(/\b(\d+)\.(\d+)\.(\d+)\b/);
330
- if (!match) return false;
331
- const [, major, minor] = match.map(Number);
332
- return major > 0 || minor >= 99;
333
- }
334
-
335
- export function listDefinitions() {
336
- return Object.values(definitions).map(publicDefinition);
337
- }
338
-
339
- function claudeToolResultText(content) {
340
- if (typeof content === "string") return content;
341
- if (!Array.isArray(content)) return content == null ? "" : JSON.stringify(content);
342
- return content.map((block) => {
343
- if (typeof block === "string") return block;
344
- if (block?.type === "text") return block.text || "";
345
- return JSON.stringify(block);
346
- }).join("");
347
- }
348
-
349
- function assistantMessageText(message) {
350
- if (!Array.isArray(message?.content)) return "";
351
- return message.content
352
- .filter((block) => block?.type === "text")
353
- .map((block) => block.text || "")
354
- .join("");
355
- }
356
-
357
- function openCodeErrorMessage(error) {
358
- if (typeof error === "string") return error;
359
- if (typeof error?.data?.message === "string") return error.data.message;
360
- if (typeof error?.message === "string") return error.message;
361
- if (typeof error?.name === "string") return error.name;
362
- return error == null ? "OpenCode error" : JSON.stringify(error);
363
- }
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
-
390
- export function getAdapter(id) {
391
- const adapter = definitions[id];
392
- if (!adapter) {
393
- throw new HitchError(`unknown harness: ${id}`, { code: "harness_not_found", exitCode: 3 });
394
- }
395
- return adapter;
396
- }
397
-
398
- export function publicDefinition(definition) {
399
- const revisionSources = definition.revision_sources || {};
400
- return {
401
- id: definition.id,
402
- display_name: definition.display_name,
403
- command: definition.command,
404
- path_env: definition.path_env,
405
- capabilities: definition.capabilities,
406
- revision_selectors: ["installed", ...Object.keys(revisionSources)],
407
- revision_sources: Object.fromEntries(Object.entries(revisionSources).map(([selector, source]) => [
408
- selector,
409
- {
410
- type: source.type,
411
- ...(source.package ? { package: source.package } : {}),
412
- ...(source.packages ? { packages: source.packages } : {}),
413
- ...(source.url ? { url: source.url } : {}),
414
- },
415
- ])),
416
- };
417
- }
418
-
419
- export function normalizeRequest(input) {
420
- const cwd = path.resolve(typeof input?.cwd === "string" && input.cwd ? input.cwd : process.cwd());
421
- const harnessRef = typeof input?.harness_ref === "string"
422
- ? input.harness_ref.trim()
423
- : typeof input?.agent === "string" && input.agent.trim()
424
- ? `${input.agent.trim()}@installed`
425
- : "";
426
- return {
427
- harness_ref: harnessRef,
428
- model: typeof input?.model === "string" ? input.model : "",
429
- cwd,
430
- workspace_mode: typeof input?.workspace_mode === "string" ? input.workspace_mode : "shared",
431
- prompt: typeof input?.prompt === "string" ? input.prompt : "",
432
- timeout_ms: input?.timeout_ms ?? 0,
433
- agent_args: Array.isArray(input?.agent_args) ? [...input.agent_args] : [],
434
- };
435
- }