agent-hitch 0.1.1 → 0.2.0

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 (92) hide show
  1. package/README.md +229 -132
  2. package/README.zh-CN.md +330 -0
  3. package/dist/bin/hitch.js +9 -0
  4. package/dist/bin/hitch.js.map +1 -0
  5. package/dist/scripts/check-release.js +25 -0
  6. package/dist/scripts/check-release.js.map +1 -0
  7. package/dist/scripts/check-syntax.js +32 -0
  8. package/dist/scripts/check-syntax.js.map +1 -0
  9. package/dist/src/adapters.js +461 -0
  10. package/dist/src/adapters.js.map +1 -0
  11. package/dist/src/artifacts.js +1081 -0
  12. package/dist/src/artifacts.js.map +1 -0
  13. package/dist/src/cli.js +699 -0
  14. package/dist/src/cli.js.map +1 -0
  15. package/dist/src/config.js +53 -0
  16. package/dist/src/config.js.map +1 -0
  17. package/dist/src/controller-runtime/hash.js +353 -0
  18. package/dist/src/controller-runtime/hash.js.map +1 -0
  19. package/dist/src/controller-runtime/store.js +323 -0
  20. package/dist/src/controller-runtime/store.js.map +1 -0
  21. package/dist/src/daemon.js +461 -0
  22. package/dist/src/daemon.js.map +1 -0
  23. package/dist/src/domain/types.js +10 -0
  24. package/dist/src/domain/types.js.map +1 -0
  25. package/dist/src/domain/validate.js +240 -0
  26. package/dist/src/domain/validate.js.map +1 -0
  27. package/dist/src/engine.js +465 -0
  28. package/dist/src/engine.js.map +1 -0
  29. package/dist/src/errors.js +14 -0
  30. package/dist/src/errors.js.map +1 -0
  31. package/dist/src/eval-tools.js +318 -0
  32. package/dist/src/eval-tools.js.map +1 -0
  33. package/dist/src/evals.js +313 -0
  34. package/dist/src/evals.js.map +1 -0
  35. package/dist/src/events.js +80 -0
  36. package/dist/src/events.js.map +1 -0
  37. package/dist/src/feedback/service.js +315 -0
  38. package/dist/src/feedback/service.js.map +1 -0
  39. package/dist/src/fs.js +45 -0
  40. package/dist/src/fs.js.map +1 -0
  41. package/dist/src/harbor-backend.js +280 -0
  42. package/dist/src/harbor-backend.js.map +1 -0
  43. package/dist/src/harness-reference.js +83 -0
  44. package/dist/src/harness-reference.js.map +1 -0
  45. package/dist/src/line-stream.js +20 -0
  46. package/dist/src/line-stream.js.map +1 -0
  47. package/dist/src/locks.js +38 -0
  48. package/dist/src/locks.js.map +1 -0
  49. package/dist/src/package-root.js +40 -0
  50. package/dist/src/package-root.js.map +1 -0
  51. package/dist/src/process.js +50 -0
  52. package/dist/src/process.js.map +1 -0
  53. package/dist/src/registry.js +81 -0
  54. package/dist/src/registry.js.map +1 -0
  55. package/dist/src/scheduler.js +162 -0
  56. package/dist/src/scheduler.js.map +1 -0
  57. package/dist/src/trajectories/contract.js +17 -0
  58. package/dist/src/trajectories/contract.js.map +1 -0
  59. package/dist/src/trajectories/format.js +127 -0
  60. package/dist/src/trajectories/format.js.map +1 -0
  61. package/dist/src/trajectories/projector.js +385 -0
  62. package/dist/src/trajectories/projector.js.map +1 -0
  63. package/dist/src/trajectories/store.js +281 -0
  64. package/dist/src/trajectories/store.js.map +1 -0
  65. package/dist/src/workspaces.js +858 -0
  66. package/dist/src/workspaces.js.map +1 -0
  67. package/docs/schemas/controller-runtime-manifest.schema.json +57 -0
  68. package/docs/schemas/controller-runtime-ref.schema.json +16 -0
  69. package/docs/schemas/message-feedback.schema.json +37 -0
  70. package/docs/schemas/trajectory-ref.schema.json +30 -0
  71. package/integrations/harbor/hitch_harbor_agent.py +176 -4
  72. package/package.json +19 -11
  73. package/bin/hitch.js +0 -9
  74. package/src/adapters.js +0 -435
  75. package/src/artifacts.js +0 -949
  76. package/src/cli.js +0 -505
  77. package/src/config.js +0 -51
  78. package/src/daemon.js +0 -416
  79. package/src/engine.js +0 -400
  80. package/src/errors.js +0 -12
  81. package/src/eval-tools.js +0 -334
  82. package/src/evals.js +0 -276
  83. package/src/events.js +0 -69
  84. package/src/fs.js +0 -43
  85. package/src/harbor-backend.js +0 -285
  86. package/src/harness-reference.js +0 -78
  87. package/src/line-stream.js +0 -17
  88. package/src/locks.js +0 -33
  89. package/src/process.js +0 -49
  90. package/src/registry.js +0 -84
  91. package/src/scheduler.js +0 -156
  92. package/src/workspaces.js +0 -893
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
- }