agent-hitch 0.1.0 → 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 +240 -126
  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 -361
  75. package/src/artifacts.js +0 -948
  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 -386
  80. package/src/errors.js +0 -12
  81. package/src/eval-tools.js +0 -331
  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,361 +0,0 @@
1
- import path from "node:path";
2
- import { HitchError } from "./errors.js";
3
-
4
- const definitions = {
5
- codex: {
6
- id: "codex",
7
- display_name: "Codex CLI",
8
- command: "codex",
9
- path_env: "HITCH_CODEX_PATH",
10
- version_args: ["--version"],
11
- revision_sources: {
12
- version: { type: "npm", package: "@openai/codex", bin: "codex" },
13
- commit: {
14
- type: "git",
15
- url: "https://github.com/openai/codex.git",
16
- commands: [
17
- { executable: "cargo", args: ["build", "--release", "--locked", "--bin", "codex"], cwd: "codex-rs" },
18
- ],
19
- entrypoint: "codex-rs/target/release/codex",
20
- },
21
- },
22
- capabilities: {
23
- non_interactive: true,
24
- streaming: true,
25
- structured_messages: true,
26
- structured_tool_events: true,
27
- sessions: true,
28
- resume: false,
29
- model_selection: true,
30
- graceful_cancel: true,
31
- },
32
- process(request, executable, runtime = {}) {
33
- const args = ["exec", "--json"];
34
- if (codexSupportsEphemeral(runtime.observed_version)) args.push("--ephemeral");
35
- args.push("--skip-git-repo-check", "--color", "never", "-C", request.cwd);
36
- if (request.model) args.push("--model", request.model);
37
- args.push(...request.agent_args, "-");
38
- return { executable, args, input: request.prompt };
39
- },
40
- translate(event) {
41
- if (event.type === "thread.started") {
42
- return [{ type: "session.created", session_id: event.thread_id }];
43
- }
44
- if (event.type === "item.completed" && event.item?.type === "agent_message") {
45
- return [{ type: "message.delta", text: event.item.text || "" }];
46
- }
47
- const toolTypes = new Set(["command_execution", "file_change", "mcp_tool_call", "web_search"]);
48
- if (event.type === "item.started" && toolTypes.has(event.item?.type)) {
49
- return [{
50
- type: "tool.started",
51
- call_id: event.item.id,
52
- name: event.item.type,
53
- native: event.item,
54
- }];
55
- }
56
- if (event.type === "item.completed" && toolTypes.has(event.item?.type)) {
57
- return [{
58
- type: "tool.completed",
59
- call_id: event.item.id,
60
- name: event.item.type,
61
- status: event.item.status || "completed",
62
- native: event.item,
63
- }];
64
- }
65
- if (event.type === "turn.completed" && event.usage) {
66
- return [{ type: "usage.updated", usage: event.usage }];
67
- }
68
- if (event.type === "error") {
69
- return [{ type: "diagnostic", level: "error", message: event.message || "Codex error" }];
70
- }
71
- return [{ type: "provider.event", provider_type: event.type || "unknown", native: event }];
72
- },
73
- },
74
- claude: {
75
- id: "claude",
76
- display_name: "Claude Code",
77
- command: "claude",
78
- path_env: "HITCH_CLAUDE_PATH",
79
- version_args: ["--version"],
80
- revision_sources: {
81
- version: { type: "npm", package: "@anthropic-ai/claude-code", bin: "claude" },
82
- },
83
- capabilities: {
84
- non_interactive: true,
85
- streaming: true,
86
- structured_messages: true,
87
- structured_tool_events: true,
88
- sessions: true,
89
- resume: false,
90
- model_selection: true,
91
- graceful_cancel: true,
92
- },
93
- process(request, executable) {
94
- const args = ["-p", "--output-format", "stream-json", "--verbose"];
95
- if (request.model) args.push("--model", request.model);
96
- args.push(...request.agent_args);
97
- return { executable, args, input: request.prompt };
98
- },
99
- translate(event) {
100
- if (event.type === "system" && event.session_id) {
101
- return [{ type: "session.created", session_id: event.session_id }];
102
- }
103
- if (event.type === "assistant") {
104
- const content = event.message?.content || [];
105
- return content.flatMap((block) => {
106
- if (block.type === "text") return [{ type: "message.delta", text: block.text || "" }];
107
- if (block.type === "tool_use") {
108
- return [{ type: "tool.started", call_id: block.id, name: block.name, input: block.input }];
109
- }
110
- return [];
111
- });
112
- }
113
- if (event.type === "user") {
114
- const content = event.message?.content || [];
115
- return content.flatMap((block) => {
116
- if (block.type !== "tool_result") return [];
117
- return [{
118
- type: "tool.completed",
119
- call_id: block.tool_use_id,
120
- status: block.is_error ? "failed" : "succeeded",
121
- output: claudeToolResultText(block.content),
122
- }];
123
- });
124
- }
125
- if (event.type === "result") {
126
- const translated = [];
127
- if (typeof event.result === "string") translated.push({ type: "message.completed", text: event.result });
128
- if (event.usage) translated.push({ type: "usage.updated", usage: event.usage });
129
- return translated;
130
- }
131
- return [{ type: "provider.event", provider_type: event.type || "unknown", native: event }];
132
- },
133
- },
134
- pi: {
135
- id: "pi",
136
- display_name: "Pi Coding Agent",
137
- command: "pi",
138
- path_env: "HITCH_PI_PATH",
139
- version_args: ["--version"],
140
- revision_sources: {
141
- version: {
142
- type: "npm",
143
- packages: ["@earendil-works/pi-coding-agent", "@mariozechner/pi-coding-agent"],
144
- bin: "pi",
145
- },
146
- commit: {
147
- type: "git",
148
- url: "https://github.com/earendil-works/pi.git",
149
- commands: [
150
- { executable: "npm", args: ["ci", "--ignore-scripts"] },
151
- { executable: "npm", args: ["run", "build"] },
152
- ],
153
- entrypoint: "packages/coding-agent/dist/cli.js",
154
- },
155
- },
156
- capabilities: {
157
- non_interactive: true,
158
- streaming: true,
159
- structured_messages: true,
160
- structured_tool_events: true,
161
- sessions: true,
162
- resume: false,
163
- model_selection: true,
164
- graceful_cancel: true,
165
- },
166
- process(request, executable) {
167
- const args = ["--mode", "json", "--no-session"];
168
- if (request.model) args.push("--model", request.model);
169
- args.push(...request.agent_args);
170
- return { executable, args, input: request.prompt };
171
- },
172
- translate(event) {
173
- if (event.type === "session" && event.id) {
174
- return [{ type: "session.created", session_id: event.id }];
175
- }
176
- if (event.type === "message_update" && event.assistantMessageEvent?.type === "text_delta") {
177
- return [{ type: "message.delta", text: event.assistantMessageEvent.delta || "" }];
178
- }
179
- if (event.type === "message_end" && event.message?.role === "assistant") {
180
- const translated = [{ type: "message.completed", text: assistantMessageText(event.message) }];
181
- if (event.message.usage) translated.push({ type: "usage.updated", usage: event.message.usage });
182
- if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
183
- translated.push({
184
- type: "diagnostic",
185
- level: "error",
186
- message: event.message.errorMessage || `Pi request ${event.message.stopReason}`,
187
- });
188
- }
189
- return translated;
190
- }
191
- if (event.type === "tool_execution_start") {
192
- return [{
193
- type: "tool.started",
194
- call_id: event.toolCallId,
195
- name: event.toolName,
196
- input: event.args,
197
- }];
198
- }
199
- if (event.type === "tool_execution_end") {
200
- return [{
201
- type: "tool.completed",
202
- call_id: event.toolCallId,
203
- name: event.toolName,
204
- status: event.isError ? "failed" : "succeeded",
205
- output: event.result,
206
- }];
207
- }
208
- return [{ type: "provider.event", provider_type: event.type || "unknown", native: event }];
209
- },
210
- },
211
- opencode: {
212
- id: "opencode",
213
- display_name: "OpenCode",
214
- command: "opencode",
215
- path_env: "HITCH_OPENCODE_PATH",
216
- version_args: ["--version"],
217
- revision_sources: {
218
- version: { type: "npm", package: "opencode-ai", bin: "opencode" },
219
- },
220
- capabilities: {
221
- non_interactive: true,
222
- streaming: true,
223
- structured_messages: true,
224
- structured_tool_events: true,
225
- sessions: true,
226
- resume: false,
227
- model_selection: true,
228
- graceful_cancel: true,
229
- },
230
- process(request, executable) {
231
- const args = ["run", "--format", "json", "--dir", request.cwd];
232
- if (request.model) args.push("--model", request.model);
233
- args.push(...request.agent_args);
234
- return { executable, args, input: request.prompt };
235
- },
236
- translate(event, state = {}) {
237
- const translated = [];
238
- if (event.sessionID && state.session_id !== event.sessionID) {
239
- state.session_id = event.sessionID;
240
- translated.push({ type: "session.created", session_id: event.sessionID });
241
- }
242
- if (event.type === "text") {
243
- translated.push({
244
- type: "message.delta",
245
- text: typeof event.part?.text === "string" ? event.part.text : event.text || "",
246
- });
247
- return translated;
248
- }
249
- if (event.type === "tool_use" && event.part) {
250
- const failed = event.part.state?.status === "error";
251
- translated.push({
252
- type: "tool.completed",
253
- call_id: event.part.callID || event.part.id,
254
- name: event.part.tool,
255
- status: failed ? "failed" : "succeeded",
256
- input: event.part.state?.input,
257
- output: failed ? event.part.state?.error : event.part.state?.output,
258
- native: event.part,
259
- });
260
- return translated;
261
- }
262
- if (event.type === "step_finish" && event.part?.tokens) {
263
- translated.push({
264
- type: "usage.updated",
265
- usage: { ...event.part.tokens, cost: event.part.cost },
266
- });
267
- return translated;
268
- }
269
- if (event.type === "error") {
270
- translated.push({ type: "diagnostic", level: "error", message: openCodeErrorMessage(event.error) });
271
- return translated;
272
- }
273
- translated.push({ type: "provider.event", provider_type: event.type || "unknown", native: event });
274
- return translated;
275
- },
276
- },
277
- };
278
-
279
- function codexSupportsEphemeral(observedVersion) {
280
- const match = String(observedVersion || "").match(/\b(\d+)\.(\d+)\.(\d+)\b/);
281
- if (!match) return false;
282
- const [, major, minor] = match.map(Number);
283
- return major > 0 || minor >= 99;
284
- }
285
-
286
- export function listDefinitions() {
287
- return Object.values(definitions).map(publicDefinition);
288
- }
289
-
290
- function claudeToolResultText(content) {
291
- if (typeof content === "string") return content;
292
- if (!Array.isArray(content)) return content == null ? "" : JSON.stringify(content);
293
- return content.map((block) => {
294
- if (typeof block === "string") return block;
295
- if (block?.type === "text") return block.text || "";
296
- return JSON.stringify(block);
297
- }).join("");
298
- }
299
-
300
- function assistantMessageText(message) {
301
- if (!Array.isArray(message?.content)) return "";
302
- return message.content
303
- .filter((block) => block?.type === "text")
304
- .map((block) => block.text || "")
305
- .join("");
306
- }
307
-
308
- function openCodeErrorMessage(error) {
309
- if (typeof error === "string") return error;
310
- if (typeof error?.data?.message === "string") return error.data.message;
311
- if (typeof error?.message === "string") return error.message;
312
- if (typeof error?.name === "string") return error.name;
313
- return error == null ? "OpenCode error" : JSON.stringify(error);
314
- }
315
-
316
- export function getAdapter(id) {
317
- const adapter = definitions[id];
318
- if (!adapter) {
319
- throw new HitchError(`unknown harness: ${id}`, { code: "harness_not_found", exitCode: 3 });
320
- }
321
- return adapter;
322
- }
323
-
324
- export function publicDefinition(definition) {
325
- const revisionSources = definition.revision_sources || {};
326
- return {
327
- id: definition.id,
328
- display_name: definition.display_name,
329
- command: definition.command,
330
- path_env: definition.path_env,
331
- capabilities: definition.capabilities,
332
- revision_selectors: ["installed", ...Object.keys(revisionSources)],
333
- revision_sources: Object.fromEntries(Object.entries(revisionSources).map(([selector, source]) => [
334
- selector,
335
- {
336
- type: source.type,
337
- ...(source.package ? { package: source.package } : {}),
338
- ...(source.packages ? { packages: source.packages } : {}),
339
- ...(source.url ? { url: source.url } : {}),
340
- },
341
- ])),
342
- };
343
- }
344
-
345
- export function normalizeRequest(input) {
346
- const cwd = path.resolve(typeof input?.cwd === "string" && input.cwd ? input.cwd : process.cwd());
347
- const harnessRef = typeof input?.harness_ref === "string"
348
- ? input.harness_ref.trim()
349
- : typeof input?.agent === "string" && input.agent.trim()
350
- ? `${input.agent.trim()}@installed`
351
- : "";
352
- return {
353
- harness_ref: harnessRef,
354
- model: typeof input?.model === "string" ? input.model : "",
355
- cwd,
356
- workspace_mode: typeof input?.workspace_mode === "string" ? input.workspace_mode : "shared",
357
- prompt: typeof input?.prompt === "string" ? input.prompt : "",
358
- timeout_ms: input?.timeout_ms ?? 0,
359
- agent_args: Array.isArray(input?.agent_args) ? [...input.agent_args] : [],
360
- };
361
- }