harness-dispatch 0.4.0 → 0.6.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.
- package/CHANGELOG.md +152 -0
- package/README.md +232 -87
- package/config.default.yaml +416 -397
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +2 -2
- package/dist/auth.js.map +1 -1
- package/dist/billing.d.ts +35 -4
- package/dist/billing.d.ts.map +1 -1
- package/dist/billing.js +48 -17
- package/dist/billing.js.map +1 -1
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +147 -91
- package/dist/bin.js.map +1 -1
- package/dist/breaker-store.d.ts +60 -9
- package/dist/breaker-store.d.ts.map +1 -1
- package/dist/breaker-store.js +246 -50
- package/dist/breaker-store.js.map +1 -1
- package/dist/circuit-breaker.d.ts +35 -0
- package/dist/circuit-breaker.d.ts.map +1 -1
- package/dist/circuit-breaker.js +68 -11
- package/dist/circuit-breaker.js.map +1 -1
- package/dist/config/coercions.d.ts +31 -0
- package/dist/config/coercions.d.ts.map +1 -0
- package/dist/config/coercions.js +169 -0
- package/dist/config/coercions.js.map +1 -0
- package/dist/config/env-interpolation.d.ts +36 -0
- package/dist/config/env-interpolation.d.ts.map +1 -0
- package/dist/config/env-interpolation.js +73 -0
- package/dist/config/env-interpolation.js.map +1 -0
- package/dist/config/protocol.d.ts +51 -0
- package/dist/config/protocol.d.ts.map +1 -0
- package/dist/config/protocol.js +300 -0
- package/dist/config/protocol.js.map +1 -0
- package/dist/config/route-fields.d.ts +72 -0
- package/dist/config/route-fields.d.ts.map +1 -0
- package/dist/config/route-fields.js +128 -0
- package/dist/config/route-fields.js.map +1 -0
- package/dist/config/validation.d.ts +72 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +189 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/config.d.ts +19 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +214 -635
- package/dist/config.js.map +1 -1
- package/dist/configure-yaml.d.ts +23 -0
- package/dist/configure-yaml.d.ts.map +1 -0
- package/dist/configure-yaml.js +193 -0
- package/dist/configure-yaml.js.map +1 -0
- package/dist/dispatch-log.d.ts.map +1 -1
- package/dist/dispatch-log.js +2 -3
- package/dist/dispatch-log.js.map +1 -1
- package/dist/dispatchers/base.d.ts +3 -0
- package/dist/dispatchers/base.d.ts.map +1 -1
- package/dist/dispatchers/base.js.map +1 -1
- package/dist/dispatchers/generic-cli.d.ts +15 -1
- package/dist/dispatchers/generic-cli.d.ts.map +1 -1
- package/dist/dispatchers/generic-cli.js +69 -5
- package/dist/dispatchers/generic-cli.js.map +1 -1
- package/dist/dispatchers/openai-compatible.d.ts.map +1 -1
- package/dist/dispatchers/openai-compatible.js +70 -2
- package/dist/dispatchers/openai-compatible.js.map +1 -1
- package/dist/dispatchers/shared/kill-tree.d.ts +17 -4
- package/dist/dispatchers/shared/kill-tree.d.ts.map +1 -1
- package/dist/dispatchers/shared/kill-tree.js +28 -4
- package/dist/dispatchers/shared/kill-tree.js.map +1 -1
- package/dist/dispatchers/shared/rate-limit-headers.d.ts +17 -0
- package/dist/dispatchers/shared/rate-limit-headers.d.ts.map +1 -1
- package/dist/dispatchers/shared/rate-limit-headers.js +27 -0
- package/dist/dispatchers/shared/rate-limit-headers.js.map +1 -1
- package/dist/dispatchers/shared/stream-subprocess.d.ts +11 -0
- package/dist/dispatchers/shared/stream-subprocess.d.ts.map +1 -1
- package/dist/dispatchers/shared/stream-subprocess.js +62 -35
- package/dist/dispatchers/shared/stream-subprocess.js.map +1 -1
- package/dist/dispatchers/shared/subprocess.d.ts.map +1 -1
- package/dist/dispatchers/shared/subprocess.js +3 -0
- package/dist/dispatchers/shared/subprocess.js.map +1 -1
- package/dist/dispatchers/shared/which-available.d.ts.map +1 -1
- package/dist/dispatchers/shared/which-available.js +16 -2
- package/dist/dispatchers/shared/which-available.js.map +1 -1
- package/dist/file-lock.d.ts +17 -0
- package/dist/file-lock.d.ts.map +1 -0
- package/dist/file-lock.js +143 -0
- package/dist/file-lock.js.map +1 -0
- package/dist/http/parse.d.ts +64 -0
- package/dist/http/parse.d.ts.map +1 -0
- package/dist/http/parse.js +230 -0
- package/dist/http/parse.js.map +1 -0
- package/dist/http/server.d.ts +2 -0
- package/dist/http/server.d.ts.map +1 -1
- package/dist/http/server.js +249 -286
- package/dist/http/server.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/job-runner.js +23 -5
- package/dist/job-runner.js.map +1 -1
- package/dist/jobs/context.d.ts +17 -0
- package/dist/jobs/context.d.ts.map +1 -0
- package/dist/jobs/context.js +84 -0
- package/dist/jobs/context.js.map +1 -0
- package/dist/jobs/store.d.ts +80 -0
- package/dist/jobs/store.d.ts.map +1 -0
- package/dist/jobs/store.js +279 -0
- package/dist/jobs/store.js.map +1 -0
- package/dist/jobs/types.d.ts +135 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +11 -0
- package/dist/jobs/types.js.map +1 -0
- package/dist/jobs.d.ts +165 -85
- package/dist/jobs.d.ts.map +1 -1
- package/dist/jobs.js +824 -208
- package/dist/jobs.js.map +1 -1
- package/dist/leaderboard.d.ts +24 -1
- package/dist/leaderboard.d.ts.map +1 -1
- package/dist/leaderboard.js +94 -2
- package/dist/leaderboard.js.map +1 -1
- package/dist/mcp/config-hot-reload.d.ts.map +1 -1
- package/dist/mcp/config-hot-reload.js +1 -1
- package/dist/mcp/config-hot-reload.js.map +1 -1
- package/dist/mcp/dispatcher-factory.d.ts +16 -1
- package/dist/mcp/dispatcher-factory.d.ts.map +1 -1
- package/dist/mcp/dispatcher-factory.js +41 -3
- package/dist/mcp/dispatcher-factory.js.map +1 -1
- package/dist/mcp/tool-schemas.d.ts +194 -0
- package/dist/mcp/tool-schemas.d.ts.map +1 -0
- package/dist/mcp/tool-schemas.js +311 -0
- package/dist/mcp/tool-schemas.js.map +1 -0
- package/dist/mcp/tools.d.ts +26 -51
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +122 -151
- package/dist/mcp/tools.js.map +1 -1
- package/dist/observability/index.d.ts +16 -3
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +23 -7
- package/dist/observability/index.js.map +1 -1
- package/dist/observability/spans.d.ts.map +1 -1
- package/dist/observability/spans.js +4 -1
- package/dist/observability/spans.js.map +1 -1
- package/dist/quota.d.ts +106 -22
- package/dist/quota.d.ts.map +1 -1
- package/dist/quota.js +198 -58
- package/dist/quota.js.map +1 -1
- package/dist/route-policy.d.ts.map +1 -1
- package/dist/route-policy.js +13 -3
- package/dist/route-policy.js.map +1 -1
- package/dist/router.d.ts +29 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +102 -40
- package/dist/router.js.map +1 -1
- package/dist/safety.d.ts.map +1 -1
- package/dist/safety.js +11 -2
- package/dist/safety.js.map +1 -1
- package/dist/state-dir.d.ts +17 -0
- package/dist/state-dir.d.ts.map +1 -0
- package/dist/state-dir.js +21 -0
- package/dist/state-dir.js.map +1 -0
- package/dist/status.d.ts +49 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +67 -4
- package/dist/status.js.map +1 -1
- package/dist/types.d.ts +74 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/working-dir.d.ts +13 -0
- package/dist/working-dir.d.ts.map +1 -1
- package/dist/working-dir.js +27 -0
- package/dist/working-dir.js.map +1 -1
- package/dist/workspace-lock.d.ts +40 -0
- package/dist/workspace-lock.d.ts.map +1 -0
- package/dist/workspace-lock.js +258 -0
- package/dist/workspace-lock.js.map +1 -0
- package/dist/workspace-resolve.d.ts +98 -0
- package/dist/workspace-resolve.d.ts.map +1 -0
- package/dist/workspace-resolve.js +382 -0
- package/dist/workspace-resolve.js.map +1 -0
- package/dist/workspaces.d.ts +16 -0
- package/dist/workspaces.d.ts.map +1 -1
- package/dist/workspaces.js +166 -22
- package/dist/workspaces.js.map +1 -1
- package/package.json +99 -96
- package/dist/dashboard/live.d.ts +0 -49
- package/dist/dashboard/live.d.ts.map +0 -1
- package/dist/dashboard/live.js +0 -149
- package/dist/dashboard/live.js.map +0 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What happens to isolated work AFTER the agent finishes.
|
|
3
|
+
*
|
|
4
|
+
* `copy` and `git_worktree` gave you a workspace path, a changed-file list and
|
|
5
|
+
* a `cleanupHint` — a string telling you to run `git worktree remove`
|
|
6
|
+
* yourself. Isolation worked and nothing was ever applied automatically, but
|
|
7
|
+
* there was no way to see the actual change, no way to keep it, and no way to
|
|
8
|
+
* clean up but by hand. Isolated dispatches were effectively write-only: you
|
|
9
|
+
* could look at a summary and then abandon the result.
|
|
10
|
+
*
|
|
11
|
+
* This module is the missing half — inspect, then keep or throw away.
|
|
12
|
+
*
|
|
13
|
+
* THE DANGEROUS PART IS `apply`, and it is dangerous in one specific way:
|
|
14
|
+
* applying a patch into a directory that has moved on since the agent started
|
|
15
|
+
* can conflict with, or silently overwrite, work the user did in the meantime.
|
|
16
|
+
* So apply refuses when the target has uncommitted changes unless the caller
|
|
17
|
+
* explicitly overrides, and it always writes the patch to disk first so there
|
|
18
|
+
* is a manual path (`git apply`) even when the automatic one declines.
|
|
19
|
+
*/
|
|
20
|
+
import { execFile as execFileCb } from "node:child_process";
|
|
21
|
+
import { existsSync } from "node:fs";
|
|
22
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
import { promisify } from "node:util";
|
|
25
|
+
import { EXCLUDED_DIRS } from "./workspaces.js";
|
|
26
|
+
const execFile = promisify(execFileCb);
|
|
27
|
+
/** Patches are read into memory and returned over MCP, so they are bounded. */
|
|
28
|
+
export const MAX_PATCH_BYTES = 2 * 1024 * 1024;
|
|
29
|
+
/** How much of a patch is returned inline before it is truncated for display. */
|
|
30
|
+
export const MAX_PATCH_CHARS = 60_000;
|
|
31
|
+
async function git(args, cwd) {
|
|
32
|
+
const { stdout } = await execFile("git", args, {
|
|
33
|
+
cwd,
|
|
34
|
+
windowsHide: true,
|
|
35
|
+
maxBuffer: MAX_PATCH_BYTES,
|
|
36
|
+
});
|
|
37
|
+
return String(stdout);
|
|
38
|
+
}
|
|
39
|
+
/** `git diff` exits 1 when there ARE differences; that is success, not failure. */
|
|
40
|
+
async function gitDiff(args, cwd) {
|
|
41
|
+
try {
|
|
42
|
+
return await git(args, cwd);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
const e = err;
|
|
46
|
+
if (e.code === 1 && typeof e.stdout === "string")
|
|
47
|
+
return e.stdout;
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** The directory the agent actually worked in, for each isolation policy. */
|
|
52
|
+
function isolatedRoot(run) {
|
|
53
|
+
if (run.policy === "git_worktree")
|
|
54
|
+
return path.join(run.workspaceRoot ?? "", "worktree");
|
|
55
|
+
return run.effectiveWorkingDir;
|
|
56
|
+
}
|
|
57
|
+
export function isResolvable(run) {
|
|
58
|
+
return run !== undefined && run.isolated && run.workspaceRoot !== undefined;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Produce a unified patch of what the agent changed, relative to the project
|
|
62
|
+
* root, so it applies with `git apply -p1` from `originalWorkingDir`.
|
|
63
|
+
*
|
|
64
|
+
* The two policies need different bases and getting this wrong is destructive:
|
|
65
|
+
*
|
|
66
|
+
* git_worktree — diff against the COMMIT the worktree was created from.
|
|
67
|
+
* A worktree starts at HEAD, not at the caller's working tree, so diffing
|
|
68
|
+
* it against the original directory of a dirty project would report the
|
|
69
|
+
* user's own uncommitted work as deletions.
|
|
70
|
+
* copy — diff against the original directory, which is exactly what the
|
|
71
|
+
* copy was made from, so the difference is the agent's work and nothing
|
|
72
|
+
* else.
|
|
73
|
+
*/
|
|
74
|
+
export async function buildWorkspacePatch(run) {
|
|
75
|
+
const root = isolatedRoot(run);
|
|
76
|
+
if (!existsSync(root)) {
|
|
77
|
+
throw new Error(`The isolated workspace for this job is gone (${root}). Workspaces are pruned ` +
|
|
78
|
+
`once they age out, so inspect and resolve a job before that happens.`);
|
|
79
|
+
}
|
|
80
|
+
if (run.policy === "git_worktree") {
|
|
81
|
+
if (!run.baseCommit) {
|
|
82
|
+
throw new Error("This worktree job predates base-commit recording, so a safe patch cannot be " +
|
|
83
|
+
"produced — diffing it against a dirty project could report your own " +
|
|
84
|
+
"uncommitted work as deletions. Inspect it by hand at " +
|
|
85
|
+
`${root}, or re-run the dispatch.`);
|
|
86
|
+
}
|
|
87
|
+
// `add -A -N` registers untracked files as intent-to-add so they appear in
|
|
88
|
+
// the diff as additions. It touches only the throwaway worktree's index.
|
|
89
|
+
await git(["add", "-A", "-N"], root).catch(() => undefined);
|
|
90
|
+
return gitDiff(["diff", "--binary", run.baseCommit, "--"], root);
|
|
91
|
+
}
|
|
92
|
+
// --no-index works outside a repository and handles added/deleted files.
|
|
93
|
+
//
|
|
94
|
+
// The paths are handed to git in POSIX form deliberately. Given a Windows
|
|
95
|
+
// path, git quotes the whole filename and escapes the separators as `\\`,
|
|
96
|
+
// so the emitted header reads `"a/C:\\Users\\..."` — and any attempt to
|
|
97
|
+
// normalise that afterwards has to understand C-string escaping. Passing
|
|
98
|
+
// forward slashes means git emits a plain unquoted path that simple prefix
|
|
99
|
+
// stripping can handle.
|
|
100
|
+
const origPosix = toPosix(run.originalWorkingDir);
|
|
101
|
+
const isoPosix = toPosix(root);
|
|
102
|
+
const raw = await gitDiff(["diff", "--no-index", "--binary", "--", origPosix, isoPosix], path.dirname(run.originalWorkingDir));
|
|
103
|
+
// Order matters: the workspace itself is filtered out on ABSOLUTE paths,
|
|
104
|
+
// before normalisation collapses them.
|
|
105
|
+
const withoutWorkspace = dropSectionsUnder(raw, toPosix(run.workspaceRoot ?? ""));
|
|
106
|
+
return dropExcludedSections(normaliseNoIndexPaths(withoutWorkspace, origPosix, isoPosix));
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Drop patch sections for files inside the workspace directory itself.
|
|
110
|
+
*
|
|
111
|
+
* A `copy` workspace lives INSIDE the project (.harness-dispatch/workspaces/),
|
|
112
|
+
* so `git diff --no-index <project> <copy>` walks into the copy while
|
|
113
|
+
* scanning the project and reports the copy's own files as project files.
|
|
114
|
+
* Normalisation then rewrites those paths — they contain the isolated root as
|
|
115
|
+
* a substring — down to the same names as the real ones, producing a patch
|
|
116
|
+
* with two conflicting sections per file: a spurious deletion of the copy's
|
|
117
|
+
* version, and the genuine edit.
|
|
118
|
+
*
|
|
119
|
+
* Observed live: `app.js` appeared twice, once "deleted file mode" and once
|
|
120
|
+
* modified. Applying that would have deleted the file it was meant to update.
|
|
121
|
+
* It never showed up in unit tests because the fixtures put the copy outside
|
|
122
|
+
* the project, which is not where the product puts it.
|
|
123
|
+
*/
|
|
124
|
+
function dropSectionsUnder(patch, absRoot) {
|
|
125
|
+
if (!patch || !absRoot)
|
|
126
|
+
return patch;
|
|
127
|
+
const lines = patch.split("\n");
|
|
128
|
+
// Section-wise, because the leaked entries cannot be told from legitimate
|
|
129
|
+
// ones by their header alone. A file the agent ADDED and a copy-of-the-copy
|
|
130
|
+
// artefact both carry the isolated root on BOTH sides of `diff --git` — the
|
|
131
|
+
// difference is direction. The artefacts are always deletions (the file
|
|
132
|
+
// exists under the workspace while scanning the project, and has no
|
|
133
|
+
// counterpart inside the copy, which excludes its own workspace directory),
|
|
134
|
+
// and a real addition is always `--- /dev/null`. Filtering on the header
|
|
135
|
+
// alone dropped every genuine change too, and produced an empty patch.
|
|
136
|
+
const sections = [];
|
|
137
|
+
let current = [];
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
if (line.startsWith("diff --git ") && current.length > 0) {
|
|
140
|
+
sections.push(current);
|
|
141
|
+
current = [];
|
|
142
|
+
}
|
|
143
|
+
current.push(line);
|
|
144
|
+
}
|
|
145
|
+
if (current.length > 0)
|
|
146
|
+
sections.push(current);
|
|
147
|
+
const kept = sections.filter((section) => {
|
|
148
|
+
const header = section[0] ?? "";
|
|
149
|
+
if (!header.startsWith("diff --git "))
|
|
150
|
+
return true;
|
|
151
|
+
const underWorkspace = header.includes(`${absRoot}/`);
|
|
152
|
+
if (!underWorkspace)
|
|
153
|
+
return true;
|
|
154
|
+
const isDeletion = section.some((l) => l.startsWith("+++ /dev/null"));
|
|
155
|
+
return !isDeletion;
|
|
156
|
+
});
|
|
157
|
+
return kept.map((s) => s.join("\n")).join("\n");
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Remove patch sections for paths the COPY never contained.
|
|
161
|
+
*
|
|
162
|
+
* copyTree skips .git, node_modules and friends, so a plain directory diff
|
|
163
|
+
* reports every one of those files as deleted — and applying that patch would
|
|
164
|
+
* delete the user's repository. The exclusion set is imported from
|
|
165
|
+
* workspaces.ts rather than restated here: two lists would drift, and the
|
|
166
|
+
* failure mode of drifting is destroying a .git directory.
|
|
167
|
+
*
|
|
168
|
+
* Only the `copy` policy needs this. A worktree diff is bounded by a real
|
|
169
|
+
* commit, so it never sees these paths at all.
|
|
170
|
+
*/
|
|
171
|
+
function dropExcludedSections(patch) {
|
|
172
|
+
if (!patch)
|
|
173
|
+
return patch;
|
|
174
|
+
const out = [];
|
|
175
|
+
let skipping = false;
|
|
176
|
+
for (const line of patch.split("\n")) {
|
|
177
|
+
if (line.startsWith("diff --git ")) {
|
|
178
|
+
const rel = /^diff --git a\/(\S+)/.exec(line)?.[1] ?? "";
|
|
179
|
+
const top = rel.split("/")[0] ?? "";
|
|
180
|
+
skipping = EXCLUDED_DIRS.has(top);
|
|
181
|
+
}
|
|
182
|
+
if (!skipping)
|
|
183
|
+
out.push(line);
|
|
184
|
+
}
|
|
185
|
+
return out.join("\n");
|
|
186
|
+
}
|
|
187
|
+
function toPosix(p) {
|
|
188
|
+
return p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Rewrite `git diff --no-index` headers so the patch is repo-relative.
|
|
192
|
+
*
|
|
193
|
+
* --no-index emits the absolute paths it was given (`--- a/C:/long/path/...`),
|
|
194
|
+
* which applies nowhere but this machine. Rewriting them to a/<rel> and
|
|
195
|
+
* b/<rel> makes the patch portable and applicable with -p1, which is what
|
|
196
|
+
* every other git patch in the world expects.
|
|
197
|
+
*/
|
|
198
|
+
export function normaliseNoIndexPaths(patch, origRoot, isoRoot) {
|
|
199
|
+
if (!patch)
|
|
200
|
+
return patch;
|
|
201
|
+
// Longest first: when one root is a prefix of the other, stripping the
|
|
202
|
+
// shorter one first would leave a fragment of the longer behind.
|
|
203
|
+
const roots = [origRoot, isoRoot].sort((a, b) => b.length - a.length);
|
|
204
|
+
return patch
|
|
205
|
+
.split("\n")
|
|
206
|
+
.map((line) => {
|
|
207
|
+
let out = line;
|
|
208
|
+
for (const r of roots) {
|
|
209
|
+
// The a/ and b/ prefixes are rebuilt explicitly rather than left to a
|
|
210
|
+
// blanket strip, because a POSIX root already starts with "/": git
|
|
211
|
+
// emits `a//tmp/proj/app.js`, and removing "/tmp/proj/" from that
|
|
212
|
+
// leaves "aapp.js" — the prefix eaten, the patch unusable. A Windows
|
|
213
|
+
// root starts with a drive letter, so the blanket version worked
|
|
214
|
+
// there and this only failed on POSIX, in CI.
|
|
215
|
+
out = out.split(`a${r}/`).join("a/").split(`b${r}/`).join("b/");
|
|
216
|
+
out = out.split(`a/${r}/`).join("a/").split(`b/${r}/`).join("b/");
|
|
217
|
+
// Anything left over (e.g. a bare path in a "similarity index" line).
|
|
218
|
+
out = out.split(`${r}/`).join("");
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
})
|
|
222
|
+
.join("\n");
|
|
223
|
+
}
|
|
224
|
+
/** Build the patch, cache it next to the job, and return it (bounded). */
|
|
225
|
+
export async function workspaceDiff(jobId, jobDir, run) {
|
|
226
|
+
const patch = await buildWorkspacePatch(run);
|
|
227
|
+
const patchPath = path.join(jobDir, "output", "workspace.patch");
|
|
228
|
+
await mkdir(path.dirname(patchPath), { recursive: true });
|
|
229
|
+
await writeFile(patchPath, patch, { encoding: "utf8", mode: 0o600 });
|
|
230
|
+
const truncated = patch.length > MAX_PATCH_CHARS;
|
|
231
|
+
return {
|
|
232
|
+
jobId,
|
|
233
|
+
policy: run.policy,
|
|
234
|
+
patchPath,
|
|
235
|
+
bytes: Buffer.byteLength(patch, "utf8"),
|
|
236
|
+
patch: truncated
|
|
237
|
+
? `${patch.slice(0, MAX_PATCH_CHARS)}\n[... truncated, full patch at ${patchPath}]`
|
|
238
|
+
: patch,
|
|
239
|
+
truncated,
|
|
240
|
+
...(run.changedFiles !== undefined ? { changedFiles: run.changedFiles } : {}),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Uncommitted changes in the target, or undefined when it is not a repo.
|
|
245
|
+
*
|
|
246
|
+
* The tool's OWN directory is not the user's work. A `copy` workspace is
|
|
247
|
+
* created at <project>/.harness-dispatch/workspaces/..., so its mere existence
|
|
248
|
+
* made `git status` non-empty and apply refused every single time with "1
|
|
249
|
+
* uncommitted change" — the feature blocking itself with its own scratch
|
|
250
|
+
* space. Only real changes count toward the dirty check.
|
|
251
|
+
*/
|
|
252
|
+
async function dirtyPaths(dir) {
|
|
253
|
+
try {
|
|
254
|
+
const out = await git(["status", "--porcelain"], dir);
|
|
255
|
+
return out
|
|
256
|
+
.split("\n")
|
|
257
|
+
.map((l) => l.trim())
|
|
258
|
+
.filter(Boolean)
|
|
259
|
+
.filter((line) => {
|
|
260
|
+
// Porcelain lines are "XY path"; the path may be quoted.
|
|
261
|
+
const p = line.slice(2).trim().replace(/^"|"$/g, "").replace(/\\/g, "/");
|
|
262
|
+
return !(p === ".harness-dispatch" || p.startsWith(".harness-dispatch/"));
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Apply the agent's work into the real project.
|
|
271
|
+
*
|
|
272
|
+
* Refuses by default when the target has uncommitted changes: the patch was
|
|
273
|
+
* built against a specific base, and applying it over work done since can
|
|
274
|
+
* conflict or clobber. `force` overrides, and the patch file is written
|
|
275
|
+
* either way so `git apply` by hand is always available.
|
|
276
|
+
*/
|
|
277
|
+
export async function applyWorkspace(jobId, jobDir, run, opts = {}) {
|
|
278
|
+
const diff = await workspaceDiff(jobId, jobDir, run);
|
|
279
|
+
if (diff.bytes === 0) {
|
|
280
|
+
return {
|
|
281
|
+
jobId,
|
|
282
|
+
applied: false,
|
|
283
|
+
patchPath: diff.patchPath,
|
|
284
|
+
message: "The agent changed nothing, so there is nothing to apply.",
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const target = run.originalWorkingDir;
|
|
288
|
+
const dirty = await dirtyPaths(target);
|
|
289
|
+
if (dirty !== undefined && dirty.length > 0 && opts.force !== true) {
|
|
290
|
+
return {
|
|
291
|
+
jobId,
|
|
292
|
+
applied: false,
|
|
293
|
+
patchPath: diff.patchPath,
|
|
294
|
+
message: `Refused: ${target} has ${dirty.length} uncommitted change(s), and this patch was ` +
|
|
295
|
+
`built against a clean base — applying it could conflict with or overwrite work ` +
|
|
296
|
+
`done since the dispatch started. Commit or stash first, re-run with force: true ` +
|
|
297
|
+
`to apply anyway, or apply by hand: git apply "${diff.patchPath}"`,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
// --3way first: it merges when context has moved instead of failing
|
|
301
|
+
// outright. It needs the pre-image blobs to exist in the target repo, which
|
|
302
|
+
// holds for a worktree patch (same repo, real commit) but NOT for a copy
|
|
303
|
+
// patch — `git diff --no-index` writes index lines for blobs the repo has
|
|
304
|
+
// never seen, so --3way fails with "could not build fake ancestor". Plain
|
|
305
|
+
// apply handles that fine, so the strict-but-smarter mode is an attempt,
|
|
306
|
+
// not a requirement.
|
|
307
|
+
let applyError;
|
|
308
|
+
for (const args of [
|
|
309
|
+
["apply", "--3way", "--whitespace=nowarn", diff.patchPath],
|
|
310
|
+
["apply", "--whitespace=nowarn", diff.patchPath],
|
|
311
|
+
]) {
|
|
312
|
+
try {
|
|
313
|
+
await git(args, target);
|
|
314
|
+
applyError = undefined;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
catch (err) {
|
|
318
|
+
applyError = err instanceof Error ? err.message : String(err);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (applyError !== undefined) {
|
|
322
|
+
const firstLine = applyError.split("\n").find((l) => l.includes("error:")) ?? applyError.split("\n")[0];
|
|
323
|
+
return {
|
|
324
|
+
jobId,
|
|
325
|
+
applied: false,
|
|
326
|
+
patchPath: diff.patchPath,
|
|
327
|
+
message: `git apply failed: ${firstLine}. The patch is intact at ` +
|
|
328
|
+
`${diff.patchPath} — resolve by hand, or inspect the workspace directly.`,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
jobId,
|
|
333
|
+
applied: true,
|
|
334
|
+
patchPath: diff.patchPath,
|
|
335
|
+
message: `Applied ${diff.bytes} bytes of changes to ${target}. Review with git diff before committing.`,
|
|
336
|
+
...(run.changedFiles !== undefined ? { changedFiles: run.changedFiles } : {}),
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Throw the isolated workspace away.
|
|
341
|
+
*
|
|
342
|
+
* A git worktree is not just a directory: `rm -rf` on it leaves the parent
|
|
343
|
+
* repository's worktree metadata behind, and git then complains about a
|
|
344
|
+
* missing worktree until someone runs `git worktree prune`. So worktrees are
|
|
345
|
+
* removed through git, with a filesystem sweep afterwards for the wrapper
|
|
346
|
+
* directory git does not own.
|
|
347
|
+
*/
|
|
348
|
+
export async function discardWorkspace(jobId, run) {
|
|
349
|
+
const root = run.workspaceRoot;
|
|
350
|
+
if (!root) {
|
|
351
|
+
return { jobId, discarded: false, message: "This job has no isolated workspace to discard." };
|
|
352
|
+
}
|
|
353
|
+
if (!existsSync(root)) {
|
|
354
|
+
return { jobId, discarded: true, message: `Already gone: ${root}` };
|
|
355
|
+
}
|
|
356
|
+
if (run.policy === "git_worktree") {
|
|
357
|
+
const worktree = path.join(root, "worktree");
|
|
358
|
+
try {
|
|
359
|
+
await git(["worktree", "remove", "--force", worktree], run.originalWorkingDir);
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
// Fall through to the filesystem sweep; prune fixes the metadata.
|
|
363
|
+
await git(["worktree", "prune"], run.originalWorkingDir).catch(() => undefined);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
await rm(root, { recursive: true, force: true });
|
|
367
|
+
return {
|
|
368
|
+
jobId,
|
|
369
|
+
discarded: true,
|
|
370
|
+
message: `Discarded the isolated workspace at ${root}. The original project was never modified.`,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
/** Read a previously written patch, if one was cached. */
|
|
374
|
+
export async function cachedPatch(jobDir) {
|
|
375
|
+
try {
|
|
376
|
+
return await readFile(path.join(jobDir, "output", "workspace.patch"), "utf8");
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
return undefined;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
//# sourceMappingURL=workspace-resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-resolve.js","sourceRoot":"","sources":["../src/workspace-resolve.ts"],"names":[],"mappings":"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"}
|
package/dist/workspaces.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DispatchResult, SafetyProfile, ServiceConfig, WorkspacePolicy } from "./types.js";
|
|
2
|
+
export declare const EXCLUDED_DIRS: Set<string>;
|
|
2
3
|
export interface PreparedWorkspace {
|
|
3
4
|
policy: WorkspacePolicy;
|
|
4
5
|
originalWorkingDir: string;
|
|
@@ -10,6 +11,21 @@ export interface PreparedWorkspace {
|
|
|
10
11
|
}
|
|
11
12
|
export declare function workspacePolicyFor(svc: ServiceConfig, safetyProfile: SafetyProfile | undefined, requestedPolicy?: WorkspacePolicy): WorkspacePolicy;
|
|
12
13
|
export declare function isIsolatedWorkspacePolicy(policy: WorkspacePolicy): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Files that sit outside `workingDir`, i.e. the ones mapFiles cannot bring
|
|
16
|
+
* into an isolated workspace.
|
|
17
|
+
*
|
|
18
|
+
* These are not merely "still readable at their original path". On CLI routes
|
|
19
|
+
* each such file's PARENT DIRECTORY is passed to the spawned agent as an
|
|
20
|
+
* access grant (generic-cli.ts includedDirectories -> {{file_dirs}} ->
|
|
21
|
+
* `--add-dir`), so under workspace_policy copy/git_worktree a single
|
|
22
|
+
* out-of-tree entry silently widens the "isolated" workspace to include a
|
|
23
|
+
* host directory. files: ["~/.ssh/id_rsa"] grants ~/.ssh.
|
|
24
|
+
*
|
|
25
|
+
* Isolation is the caller's stated intent, so this is surfaced as a warning
|
|
26
|
+
* rather than silently honoured or silently dropped.
|
|
27
|
+
*/
|
|
28
|
+
export declare function escapedFiles(files: string[], originalWorkingDir: string): string[];
|
|
13
29
|
export declare function prepareWorkspace(opts: {
|
|
14
30
|
routeName: string;
|
|
15
31
|
policy: WorkspacePolicy;
|
package/dist/workspaces.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../src/workspaces.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workspaces.d.ts","sourceRoot":"","sources":["../src/workspaces.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EAEb,eAAe,EAEhB,MAAM,YAAY,CAAC;AAIpB,eAAO,MAAM,aAAa,aAcxB,CAAC;AASH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,eAAe,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACzD;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,aAAa,EAClB,aAAa,EAAE,aAAa,GAAG,SAAS,EACxC,eAAe,CAAC,EAAE,eAAe,GAChC,eAAe,CAIjB;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAE1E;AAkSD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,kBAAkB,EAAE,MAAM,GAAG,MAAM,EAAE,CAUlF;AA6KD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAU7B"}
|