@zhivex-ai/harness 1.5.0 → 1.6.2
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 +20 -0
- package/README.md +1 -1
- package/SUPPORT.md +1 -1
- package/contracts/stable-api-signatures.json +7 -7
- package/dist/acp-cli.js +7 -7
- package/dist/approvals/auto-approve.d.ts +137 -0
- package/dist/audit/index.js +2 -2
- package/dist/{audit-pack-kvw7ng34.js → audit-pack-fckpqfsb.js} +3 -3
- package/dist/{cli-vyrytn3m.js → cli-84en174d.js} +6 -6
- package/dist/{cli-entry-q8bgkn9m.js → cli-entry-10th5awp.js} +16 -15
- package/dist/{cli-entry-h2yc9x37.js → cli-entry-4g2ewcnw.js} +10 -2
- package/dist/{cli-entry-928p8h41.js → cli-entry-6amx4mcc.js} +75 -11
- package/dist/{cli-entry-75j3r49b.js → cli-entry-z1pbjrg4.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/compat/code-support.js +5 -5
- package/dist/engine/acp.js +8 -8
- package/dist/engine/client.js +7 -7
- package/dist/engine/desktop-state-v1.js +4 -4
- package/dist/engine/index.js +10 -10
- package/dist/engine/mcp-stdio-v1.js +4 -4
- package/dist/engine/service.js +8 -8
- package/dist/hostile-repository-demo.js +98 -25
- package/dist/{index-96ezrvg2.js → index-1pn0ecs4.js} +2 -2
- package/dist/{index-5zbq9ztj.js → index-2r4f0p4n.js} +1 -1
- package/dist/{index-63gbp0cz.js → index-4mn6m3bq.js} +1 -1
- package/dist/{index-vvzc5ezz.js → index-74f9yhfh.js} +1 -1
- package/dist/{index-wwthv10a.js → index-amjttjaj.js} +1 -1
- package/dist/{index-vc7yxz50.js → index-paf7z8kn.js} +4 -4
- package/dist/{index-6fp7kdpx.js → index-pf73y4gt.js} +12 -4
- package/dist/{index-xfedewpt.js → index-rgqzdgv6.js} +2 -2
- package/dist/{index-pq3kz1r8.js → index-rz7wmp71.js} +16 -15
- package/dist/{index-ggstwr0v.js → index-w0m1nckc.js} +74 -10
- package/dist/{index-jpdvn09j.js → index-zrz4rs7p.js} +1 -1
- package/dist/index.js +9 -9
- package/dist/providers/bedrock.d.ts +1 -1
- package/dist/review/reveal-invisible.d.ts +13 -0
- package/dist/service-cli.js +8 -8
- package/dist/{service-client-cli-v6qptg9x.js → service-client-cli-pp5z4dmf.js} +3 -3
- package/dist/verify-historical-migrations.js +98 -25
- package/dist/workspace/text-integrity.d.ts +19 -0
- package/dist/workspace/workspace.d.ts +8 -3
- package/dist/zhx.js +2 -2
- package/docs/AUTO_APPROVE.md +51 -0
- package/docs/AZURE_OPENAI.md +4 -4
- package/docs/BEDROCK.md +4 -4
- package/docs/CLI.md +13 -0
- package/docs/CODE_RELEASE.md +8 -0
- package/docs/CREDENTIALS.md +1 -1
- package/docs/DURABLE_OPERATIONS.md +1 -1
- package/docs/ENGINE_API.md +1 -1
- package/docs/EXTENSIBILITY.md +1 -1
- package/docs/FIRST_USE.md +6 -6
- package/docs/MINIMUM_TASK_AUTHORITY.md +5 -5
- package/docs/PROJECT_MEMORY.md +1 -1
- package/docs/TOOL_POLICY.md +3 -0
- package/docs/VERTEX.md +2 -2
- package/docs/WEB_LIMITS.md +2 -2
- package/docs/adr/0002-isolated-mcp-stdio.md +1 -1
- package/evaluations/representative-assembly-matrix.json +28 -1
- package/examples/README.md +3 -3
- package/package.json +16 -15
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// package.json
|
|
2
2
|
var package_default = {
|
|
3
3
|
name: "@zhivex-ai/harness",
|
|
4
|
-
version: "1.
|
|
4
|
+
version: "1.6.2",
|
|
5
5
|
description: "Governed, provider-portable runtime for coding agents with durable approvals and isolated repository execution.",
|
|
6
6
|
type: "module",
|
|
7
7
|
license: "MIT",
|
|
@@ -101,6 +101,7 @@ var package_default = {
|
|
|
101
101
|
"LICENSE",
|
|
102
102
|
"docs/CHANGE_ENVELOPES.md",
|
|
103
103
|
"docs/CLI.md",
|
|
104
|
+
"docs/AUTO_APPROVE.md",
|
|
104
105
|
"docs/WEB_LIMITS.md",
|
|
105
106
|
"docs/CODE_RELEASE.md",
|
|
106
107
|
"docs/CODE_WEB_TASKS.md",
|
|
@@ -217,26 +218,26 @@ var package_default = {
|
|
|
217
218
|
"smoke:mcp:installed": "bun run build && bun scripts/mcp-installed-smoke.ts"
|
|
218
219
|
},
|
|
219
220
|
dependencies: {
|
|
220
|
-
"@zhivex-ai/agents": "1.
|
|
221
|
-
"@zhivex-ai/anthropic": "0.
|
|
222
|
-
"@zhivex-ai/azure-openai": "0.
|
|
223
|
-
"@zhivex-ai/bedrock": "2.0
|
|
224
|
-
"@zhivex-ai/core": "1.
|
|
225
|
-
"@zhivex-ai/gemini": "0.
|
|
226
|
-
"@zhivex-ai/meta": "0.
|
|
227
|
-
"@zhivex-ai/openai": "0.
|
|
228
|
-
"@zhivex-ai/qwen": "0.
|
|
229
|
-
"@zhivex-ai/vertex": "1.
|
|
230
|
-
"google-auth-library": "11.1
|
|
231
|
-
zod: "4.6.
|
|
221
|
+
"@zhivex-ai/agents": "1.12.0",
|
|
222
|
+
"@zhivex-ai/anthropic": "0.15.0",
|
|
223
|
+
"@zhivex-ai/azure-openai": "0.8.0",
|
|
224
|
+
"@zhivex-ai/bedrock": "2.1.0",
|
|
225
|
+
"@zhivex-ai/core": "1.36.0",
|
|
226
|
+
"@zhivex-ai/gemini": "0.14.0",
|
|
227
|
+
"@zhivex-ai/meta": "0.3.0",
|
|
228
|
+
"@zhivex-ai/openai": "0.17.0",
|
|
229
|
+
"@zhivex-ai/qwen": "0.18.0",
|
|
230
|
+
"@zhivex-ai/vertex": "1.3.0",
|
|
231
|
+
"google-auth-library": "11.3.1",
|
|
232
|
+
zod: "4.6.7"
|
|
232
233
|
},
|
|
233
234
|
overrides: {
|
|
234
|
-
"@zhivex-ai/core": "1.
|
|
235
|
+
"@zhivex-ai/core": "1.36.0"
|
|
235
236
|
},
|
|
236
237
|
devDependencies: {
|
|
237
238
|
"@agentclientprotocol/sdk": "1.8.0",
|
|
238
239
|
"@modelcontextprotocol/server": "2.3.1",
|
|
239
|
-
"@types/bun": "1.4.
|
|
240
|
+
"@types/bun": "1.4.3",
|
|
240
241
|
"@types/node": "^26.6.5",
|
|
241
242
|
typescript: "7.0.2",
|
|
242
243
|
"typescript-compiler-api": "npm:typescript@6.0.3"
|
|
@@ -403,6 +403,55 @@ import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
|
403
403
|
import { link, lstat as lstat3, mkdir as mkdir2, open as open2, readdir, realpath as realpath2, rename, stat, unlink } from "node:fs/promises";
|
|
404
404
|
import path3 from "node:path";
|
|
405
405
|
|
|
406
|
+
// src/workspace/text-integrity.ts
|
|
407
|
+
var controlLabel = (code) => code === 0 ? "NUL byte" : `U+${code.toString(16).padStart(4, "0")}`;
|
|
408
|
+
var firstDisallowedControl = (text) => {
|
|
409
|
+
let line = 1;
|
|
410
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
411
|
+
const code = text.charCodeAt(index);
|
|
412
|
+
if (code === 10) {
|
|
413
|
+
line += 1;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (code === 9 || code === 13)
|
|
417
|
+
continue;
|
|
418
|
+
if (code < 32 || code === 127 || code >= 128 && code <= 159)
|
|
419
|
+
return { line, label: controlLabel(code) };
|
|
420
|
+
}
|
|
421
|
+
return;
|
|
422
|
+
};
|
|
423
|
+
var firstDisallowedControlBytes = (bytes) => {
|
|
424
|
+
let line = 1;
|
|
425
|
+
for (let index = 0;index < bytes.length; index += 1) {
|
|
426
|
+
const code = bytes[index] ?? 0;
|
|
427
|
+
if (code === 10) {
|
|
428
|
+
line += 1;
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (code === 9 || code === 13)
|
|
432
|
+
continue;
|
|
433
|
+
if (code < 32 || code === 127)
|
|
434
|
+
return { line, label: controlLabel(code) };
|
|
435
|
+
}
|
|
436
|
+
return;
|
|
437
|
+
};
|
|
438
|
+
var refuseControlMessage = (hit) => `Refusing to write a control byte into a text file: ${hit.label} at L${hit.line}. The file was not changed. Remove the control byte.`;
|
|
439
|
+
var binaryAfterEditMessage = (hit) => `file is binary after edit: ${hit.label} at L${hit.line}. The edit was reverted. Read the file again before retrying.`;
|
|
440
|
+
var textGuards = {
|
|
441
|
+
beforeWrite(content, allowControlBytes) {
|
|
442
|
+
if (allowControlBytes)
|
|
443
|
+
return;
|
|
444
|
+
const problem = firstDisallowedControl(content);
|
|
445
|
+
if (problem)
|
|
446
|
+
throw new HarnessWorkspaceError(refuseControlMessage(problem));
|
|
447
|
+
},
|
|
448
|
+
afterWrite(bytes, allowControlBytes) {
|
|
449
|
+
if (allowControlBytes)
|
|
450
|
+
return;
|
|
451
|
+
return firstDisallowedControlBytes(bytes);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
406
455
|
// src/workspace/path-policy.ts
|
|
407
456
|
var HARD_IGNORES = new Set([".git", ".next", ".turbo", ".zhivex-harness", "coverage", "dist", "node_modules"]);
|
|
408
457
|
var isSensitiveName = (name) => {
|
|
@@ -732,8 +781,11 @@ class Workspace {
|
|
|
732
781
|
maxBytes: MAX_FILE_BYTES
|
|
733
782
|
});
|
|
734
783
|
const { contents } = file;
|
|
735
|
-
if (!allowBinary && contents.includes(0))
|
|
736
|
-
|
|
784
|
+
if (!allowBinary && contents.includes(0)) {
|
|
785
|
+
const hit = firstDisallowedControlBytes(contents);
|
|
786
|
+
const where = hit ? `: ${hit.label} at L${hit.line}` : "";
|
|
787
|
+
throw new HarnessWorkspaceError(`The file appears to be binary and cannot be read as text${where}.`);
|
|
788
|
+
}
|
|
737
789
|
return {
|
|
738
790
|
path: wirePath(path3.relative(this.root, safe.path)),
|
|
739
791
|
absolutePath: safe.path,
|
|
@@ -1400,19 +1452,23 @@ class Workspace {
|
|
|
1400
1452
|
if (position < 0 || text.indexOf(edit.oldText, position + 1) >= 0) {
|
|
1401
1453
|
throw new HarnessWorkspaceError("Replacement requires exactly one literal match; include more surrounding context.");
|
|
1402
1454
|
}
|
|
1403
|
-
return [{
|
|
1455
|
+
return [{
|
|
1456
|
+
path: edit.path,
|
|
1457
|
+
expectedDigest: edit.expectedDigest,
|
|
1458
|
+
content: text.slice(0, position) + edit.newText + text.slice(position + edit.oldText.length)
|
|
1459
|
+
}];
|
|
1404
1460
|
}
|
|
1405
|
-
async applyReplacement(input) {
|
|
1461
|
+
async applyReplacement(input, host = {}) {
|
|
1406
1462
|
const changes = await this.replacementChanges(input);
|
|
1407
|
-
return this.applyPatch({ proposalId: createEditProposal({ changes }).proposalId, changes });
|
|
1463
|
+
return this.applyPatch({ proposalId: createEditProposal({ changes }).proposalId, changes }, host);
|
|
1408
1464
|
}
|
|
1409
|
-
async applyPatch(input) {
|
|
1410
|
-
return this.applyPatchWithModes(input, new Map);
|
|
1465
|
+
async applyPatch(input, host = {}) {
|
|
1466
|
+
return this.applyPatchWithModes(input, new Map, undefined, host);
|
|
1411
1467
|
}
|
|
1412
|
-
async applyPatchWithModes(input, modes, assertActive) {
|
|
1413
|
-
return withWorkspaceMutation(this.root, () => this.applyPatchWithModesLocked(input, modes, assertActive));
|
|
1468
|
+
async applyPatchWithModes(input, modes, assertActive, host = {}) {
|
|
1469
|
+
return withWorkspaceMutation(this.root, () => this.applyPatchWithModesLocked(input, modes, assertActive, host));
|
|
1414
1470
|
}
|
|
1415
|
-
async applyPatchWithModesLocked(input, modes, assertActive) {
|
|
1471
|
+
async applyPatchWithModesLocked(input, modes, assertActive, host = {}) {
|
|
1416
1472
|
const proposal = validateEditProposal(input);
|
|
1417
1473
|
this.checkAuditCapacity(proposal.changes.length);
|
|
1418
1474
|
const targetPaths = new Set(proposal.changes.map((change) => change.path));
|
|
@@ -1457,6 +1513,7 @@ class Workspace {
|
|
|
1457
1513
|
throw new HarnessWorkspaceError(`Stale patch mode rejected for ${change.path}: expected ${modeBinding.beforeMode.toString(8)}, found ${before.mode.toString(8)}.`);
|
|
1458
1514
|
}
|
|
1459
1515
|
}
|
|
1516
|
+
textGuards.beforeWrite(change.content, host.allowControlBytes === true);
|
|
1460
1517
|
const temporary = await this.stageFile(safe.path, change.content, modeBinding?.afterMode ?? before?.mode ?? 420);
|
|
1461
1518
|
prepared.push({
|
|
1462
1519
|
change,
|
|
@@ -1496,6 +1553,13 @@ class Workspace {
|
|
|
1496
1553
|
await unlink(item.temporary);
|
|
1497
1554
|
}
|
|
1498
1555
|
committed.push(item);
|
|
1556
|
+
const published = await readRegularFileNoFollow(item.target, {
|
|
1557
|
+
label: "The published file",
|
|
1558
|
+
maxBytes: MAX_FILE_BYTES
|
|
1559
|
+
});
|
|
1560
|
+
const corrupt = textGuards.afterWrite(published.contents, host.allowControlBytes === true);
|
|
1561
|
+
if (corrupt)
|
|
1562
|
+
throw new HarnessWorkspaceError(binaryAfterEditMessage(corrupt));
|
|
1499
1563
|
await assertActive?.();
|
|
1500
1564
|
}
|
|
1501
1565
|
} catch (error) {
|
package/dist/index.js
CHANGED
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
inspectHarnessState,
|
|
64
64
|
readHarnessStateBackup,
|
|
65
65
|
stateBackupBundleSchema
|
|
66
|
-
} from "./index-
|
|
66
|
+
} from "./index-rgqzdgv6.js";
|
|
67
67
|
import {
|
|
68
68
|
CLI_COMMAND_OPTION_CONTRACTS,
|
|
69
69
|
CLI_OPTION_DEFINITIONS,
|
|
@@ -109,13 +109,13 @@ import {
|
|
|
109
109
|
recoverHarnessLocalService,
|
|
110
110
|
requestHarnessLocalService,
|
|
111
111
|
startHarnessLocalService
|
|
112
|
-
} from "./index-
|
|
112
|
+
} from "./index-74f9yhfh.js";
|
|
113
113
|
import {
|
|
114
114
|
createHarnessClientAdapter
|
|
115
|
-
} from "./index-
|
|
115
|
+
} from "./index-paf7z8kn.js";
|
|
116
116
|
import {
|
|
117
117
|
openWorkspaceCheckpointStore
|
|
118
|
-
} from "./index-
|
|
118
|
+
} from "./index-4mn6m3bq.js";
|
|
119
119
|
import {
|
|
120
120
|
DEFAULT_HARNESS_CONTEXT_MANIFEST,
|
|
121
121
|
DEFAULT_HARNESS_PROJECT_INSTRUCTIONS,
|
|
@@ -154,7 +154,7 @@ import {
|
|
|
154
154
|
runHarness,
|
|
155
155
|
runHarnessReviewGroup,
|
|
156
156
|
selectHarnessModel
|
|
157
|
-
} from "./index-
|
|
157
|
+
} from "./index-pf73y4gt.js";
|
|
158
158
|
import {
|
|
159
159
|
HARNESS_OPERATIONS_SCHEMA_VERSION,
|
|
160
160
|
HARNESS_SQLITE_FILE,
|
|
@@ -165,7 +165,7 @@ import {
|
|
|
165
165
|
listHarnessRuns,
|
|
166
166
|
migrateLegacyFileRuns,
|
|
167
167
|
openHarnessPersistence
|
|
168
|
-
} from "./index-
|
|
168
|
+
} from "./index-amjttjaj.js";
|
|
169
169
|
import {
|
|
170
170
|
HARNESS_MCP_CONFIG_SCHEMA_VERSION,
|
|
171
171
|
HARNESS_MCP_PERMISSIONS,
|
|
@@ -173,7 +173,7 @@ import {
|
|
|
173
173
|
createHttpMcpClient,
|
|
174
174
|
loadHarnessMcpConfiguration,
|
|
175
175
|
normalizeHarnessMcpConfiguration
|
|
176
|
-
} from "./index-
|
|
176
|
+
} from "./index-2r4f0p4n.js";
|
|
177
177
|
import {
|
|
178
178
|
CliOciRuntimeAdapter,
|
|
179
179
|
HARNESS_EXECUTION_ARTIFACT_SCHEMA_VERSION,
|
|
@@ -185,7 +185,7 @@ import {
|
|
|
185
185
|
describeOciCommand,
|
|
186
186
|
executionFingerprintInput,
|
|
187
187
|
harnessExecutionSession
|
|
188
|
-
} from "./index-
|
|
188
|
+
} from "./index-w0m1nckc.js";
|
|
189
189
|
import {
|
|
190
190
|
BUILTIN_PROVIDER_REGISTRATIONS,
|
|
191
191
|
DEFAULT_ALLOWED_CHECKS,
|
|
@@ -231,7 +231,7 @@ import {
|
|
|
231
231
|
BUN_ENGINE_RANGE,
|
|
232
232
|
HARNESS_VERSION,
|
|
233
233
|
NODE_ENGINE_RANGE
|
|
234
|
-
} from "./index-
|
|
234
|
+
} from "./index-rz7wmp71.js";
|
|
235
235
|
import {
|
|
236
236
|
EDIT_CONTRACT_SCHEMA_VERSION,
|
|
237
237
|
EDIT_DIGEST_ALGORITHM,
|
|
@@ -4,7 +4,7 @@ export declare const BEDROCK_TRANSPORT_ENVIRONMENT: readonly ["AWS_REGION", "AWS
|
|
|
4
4
|
export declare const BEDROCK_CREDENTIAL_ENVIRONMENT: readonly ["BEDROCK_API_KEY", "AWS_BEARER_TOKEN_BEDROCK"];
|
|
5
5
|
/**
|
|
6
6
|
* SDK matrix: Converse is Tier B and `runtime: "openai"` is Tier A.
|
|
7
|
-
* Published `@zhivex-ai/bedrock@2.0
|
|
7
|
+
* Published `@zhivex-ai/bedrock@2.1.0` reports those labels. Converse includes
|
|
8
8
|
* reasoning and `toolChoiceNone`. Harness reports the installed label and
|
|
9
9
|
* does not relabel the model.
|
|
10
10
|
*/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface RevealedPiece {
|
|
2
|
+
text: string;
|
|
3
|
+
/** The text stands in for a character a terminal or browser would hide. */
|
|
4
|
+
mark: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Show control, zero-width, and bidi characters, and mark tabs and trailing whitespace.
|
|
8
|
+
* Newlines stay newlines so a diff line remains one row. A second pass leaves the result unchanged.
|
|
9
|
+
*/
|
|
10
|
+
export declare const revealInvisiblePieces: (line: string) => RevealedPiece[];
|
|
11
|
+
export declare const revealInvisible: (line: string) => string;
|
|
12
|
+
/** Reveal each line and keep the newline structure of a whole file. */
|
|
13
|
+
export declare const revealInvisibleText: (value: string) => string;
|
package/dist/service-cli.js
CHANGED
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
import {
|
|
3
3
|
recoverHarnessLocalService,
|
|
4
4
|
startHarnessLocalService
|
|
5
|
-
} from "./index-
|
|
6
|
-
import"./index-
|
|
7
|
-
import"./index-
|
|
5
|
+
} from "./index-74f9yhfh.js";
|
|
6
|
+
import"./index-paf7z8kn.js";
|
|
7
|
+
import"./index-4mn6m3bq.js";
|
|
8
8
|
import {
|
|
9
9
|
createHarness
|
|
10
|
-
} from "./index-
|
|
11
|
-
import"./index-
|
|
12
|
-
import"./index-
|
|
13
|
-
import"./index-
|
|
10
|
+
} from "./index-pf73y4gt.js";
|
|
11
|
+
import"./index-amjttjaj.js";
|
|
12
|
+
import"./index-2r4f0p4n.js";
|
|
13
|
+
import"./index-w0m1nckc.js";
|
|
14
14
|
import"./index-1265j7t2.js";
|
|
15
15
|
import"./index-c39197wq.js";
|
|
16
16
|
import"./index-mzydbzp8.js";
|
|
17
17
|
import"./index-bt2p8x7r.js";
|
|
18
18
|
import"./index-fqqz1yt8.js";
|
|
19
|
-
import"./index-
|
|
19
|
+
import"./index-rz7wmp71.js";
|
|
20
20
|
import"./index-6dxrbnxm.js";
|
|
21
21
|
import"./index-4gt045j0.js";
|
|
22
22
|
import"./index-bpc596v3.js";
|
|
@@ -9,17 +9,17 @@ import {
|
|
|
9
9
|
navigateConsole,
|
|
10
10
|
printPolicyInspection,
|
|
11
11
|
sanitizeTerminalText
|
|
12
|
-
} from "./cli-entry-
|
|
12
|
+
} from "./cli-entry-4g2ewcnw.js";
|
|
13
13
|
import {
|
|
14
14
|
CLI_EVENT_SCHEMA_VERSION,
|
|
15
15
|
CLI_JSON_SCHEMA_VERSION,
|
|
16
16
|
changeEnvelopeSchema,
|
|
17
17
|
harnessPolicyDecisionEventSchema,
|
|
18
18
|
serializeStreamResult
|
|
19
|
-
} from "./cli-entry-
|
|
19
|
+
} from "./cli-entry-6amx4mcc.js";
|
|
20
20
|
import {
|
|
21
21
|
HARNESS_VERSION
|
|
22
|
-
} from "./cli-entry-
|
|
22
|
+
} from "./cli-entry-10th5awp.js";
|
|
23
23
|
import {
|
|
24
24
|
HARNESS_ERROR_CODES,
|
|
25
25
|
HARNESS_ERROR_SCHEMA_VERSION,
|
|
@@ -5905,6 +5905,55 @@ var runPortableProcess = async (command, options = {}) => {
|
|
|
5905
5905
|
}
|
|
5906
5906
|
};
|
|
5907
5907
|
|
|
5908
|
+
// src/workspace/text-integrity.ts
|
|
5909
|
+
var controlLabel = (code) => code === 0 ? "NUL byte" : `U+${code.toString(16).padStart(4, "0")}`;
|
|
5910
|
+
var firstDisallowedControl = (text2) => {
|
|
5911
|
+
let line = 1;
|
|
5912
|
+
for (let index = 0;index < text2.length; index += 1) {
|
|
5913
|
+
const code = text2.charCodeAt(index);
|
|
5914
|
+
if (code === 10) {
|
|
5915
|
+
line += 1;
|
|
5916
|
+
continue;
|
|
5917
|
+
}
|
|
5918
|
+
if (code === 9 || code === 13)
|
|
5919
|
+
continue;
|
|
5920
|
+
if (code < 32 || code === 127 || code >= 128 && code <= 159)
|
|
5921
|
+
return { line, label: controlLabel(code) };
|
|
5922
|
+
}
|
|
5923
|
+
return;
|
|
5924
|
+
};
|
|
5925
|
+
var firstDisallowedControlBytes = (bytes) => {
|
|
5926
|
+
let line = 1;
|
|
5927
|
+
for (let index = 0;index < bytes.length; index += 1) {
|
|
5928
|
+
const code = bytes[index] ?? 0;
|
|
5929
|
+
if (code === 10) {
|
|
5930
|
+
line += 1;
|
|
5931
|
+
continue;
|
|
5932
|
+
}
|
|
5933
|
+
if (code === 9 || code === 13)
|
|
5934
|
+
continue;
|
|
5935
|
+
if (code < 32 || code === 127)
|
|
5936
|
+
return { line, label: controlLabel(code) };
|
|
5937
|
+
}
|
|
5938
|
+
return;
|
|
5939
|
+
};
|
|
5940
|
+
var refuseControlMessage = (hit) => `Refusing to write a control byte into a text file: ${hit.label} at L${hit.line}. The file was not changed. Remove the control byte.`;
|
|
5941
|
+
var binaryAfterEditMessage = (hit) => `file is binary after edit: ${hit.label} at L${hit.line}. The edit was reverted. Read the file again before retrying.`;
|
|
5942
|
+
var textGuards = {
|
|
5943
|
+
beforeWrite(content, allowControlBytes) {
|
|
5944
|
+
if (allowControlBytes)
|
|
5945
|
+
return;
|
|
5946
|
+
const problem = firstDisallowedControl(content);
|
|
5947
|
+
if (problem)
|
|
5948
|
+
throw new HarnessWorkspaceError(refuseControlMessage(problem));
|
|
5949
|
+
},
|
|
5950
|
+
afterWrite(bytes, allowControlBytes) {
|
|
5951
|
+
if (allowControlBytes)
|
|
5952
|
+
return;
|
|
5953
|
+
return firstDisallowedControlBytes(bytes);
|
|
5954
|
+
}
|
|
5955
|
+
};
|
|
5956
|
+
|
|
5908
5957
|
// src/workspace/path-policy.ts
|
|
5909
5958
|
var HARD_IGNORES = new Set([".git", ".next", ".turbo", ".zhivex-harness", "coverage", "dist", "node_modules"]);
|
|
5910
5959
|
var isSensitiveName = (name) => {
|
|
@@ -6234,8 +6283,11 @@ class Workspace {
|
|
|
6234
6283
|
maxBytes: MAX_FILE_BYTES
|
|
6235
6284
|
});
|
|
6236
6285
|
const { contents } = file;
|
|
6237
|
-
if (!allowBinary && contents.includes(0))
|
|
6238
|
-
|
|
6286
|
+
if (!allowBinary && contents.includes(0)) {
|
|
6287
|
+
const hit = firstDisallowedControlBytes(contents);
|
|
6288
|
+
const where = hit ? `: ${hit.label} at L${hit.line}` : "";
|
|
6289
|
+
throw new HarnessWorkspaceError(`The file appears to be binary and cannot be read as text${where}.`);
|
|
6290
|
+
}
|
|
6239
6291
|
return {
|
|
6240
6292
|
path: wirePath(path11.relative(this.root, safe.path)),
|
|
6241
6293
|
absolutePath: safe.path,
|
|
@@ -6902,19 +6954,23 @@ class Workspace {
|
|
|
6902
6954
|
if (position < 0 || text2.indexOf(edit.oldText, position + 1) >= 0) {
|
|
6903
6955
|
throw new HarnessWorkspaceError("Replacement requires exactly one literal match; include more surrounding context.");
|
|
6904
6956
|
}
|
|
6905
|
-
return [{
|
|
6957
|
+
return [{
|
|
6958
|
+
path: edit.path,
|
|
6959
|
+
expectedDigest: edit.expectedDigest,
|
|
6960
|
+
content: text2.slice(0, position) + edit.newText + text2.slice(position + edit.oldText.length)
|
|
6961
|
+
}];
|
|
6906
6962
|
}
|
|
6907
|
-
async applyReplacement(input) {
|
|
6963
|
+
async applyReplacement(input, host = {}) {
|
|
6908
6964
|
const changes = await this.replacementChanges(input);
|
|
6909
|
-
return this.applyPatch({ proposalId: createEditProposal({ changes }).proposalId, changes });
|
|
6965
|
+
return this.applyPatch({ proposalId: createEditProposal({ changes }).proposalId, changes }, host);
|
|
6910
6966
|
}
|
|
6911
|
-
async applyPatch(input) {
|
|
6912
|
-
return this.applyPatchWithModes(input, new Map);
|
|
6967
|
+
async applyPatch(input, host = {}) {
|
|
6968
|
+
return this.applyPatchWithModes(input, new Map, undefined, host);
|
|
6913
6969
|
}
|
|
6914
|
-
async applyPatchWithModes(input, modes, assertActive) {
|
|
6915
|
-
return withWorkspaceMutation(this.root, () => this.applyPatchWithModesLocked(input, modes, assertActive));
|
|
6970
|
+
async applyPatchWithModes(input, modes, assertActive, host = {}) {
|
|
6971
|
+
return withWorkspaceMutation(this.root, () => this.applyPatchWithModesLocked(input, modes, assertActive, host));
|
|
6916
6972
|
}
|
|
6917
|
-
async applyPatchWithModesLocked(input, modes, assertActive) {
|
|
6973
|
+
async applyPatchWithModesLocked(input, modes, assertActive, host = {}) {
|
|
6918
6974
|
const proposal = validateEditProposal(input);
|
|
6919
6975
|
this.checkAuditCapacity(proposal.changes.length);
|
|
6920
6976
|
const targetPaths = new Set(proposal.changes.map((change) => change.path));
|
|
@@ -6959,6 +7015,7 @@ class Workspace {
|
|
|
6959
7015
|
throw new HarnessWorkspaceError(`Stale patch mode rejected for ${change.path}: expected ${modeBinding.beforeMode.toString(8)}, found ${before.mode.toString(8)}.`);
|
|
6960
7016
|
}
|
|
6961
7017
|
}
|
|
7018
|
+
textGuards.beforeWrite(change.content, host.allowControlBytes === true);
|
|
6962
7019
|
const temporary = await this.stageFile(safe.path, change.content, modeBinding?.afterMode ?? before?.mode ?? 420);
|
|
6963
7020
|
prepared.push({
|
|
6964
7021
|
change,
|
|
@@ -6998,6 +7055,13 @@ class Workspace {
|
|
|
6998
7055
|
await unlink(item.temporary);
|
|
6999
7056
|
}
|
|
7000
7057
|
committed.push(item);
|
|
7058
|
+
const published = await readRegularFileNoFollow(item.target, {
|
|
7059
|
+
label: "The published file",
|
|
7060
|
+
maxBytes: MAX_FILE_BYTES
|
|
7061
|
+
});
|
|
7062
|
+
const corrupt = textGuards.afterWrite(published.contents, host.allowControlBytes === true);
|
|
7063
|
+
if (corrupt)
|
|
7064
|
+
throw new HarnessWorkspaceError(binaryAfterEditMessage(corrupt));
|
|
7001
7065
|
await assertActive?.();
|
|
7002
7066
|
}
|
|
7003
7067
|
} catch (error) {
|
|
@@ -13534,6 +13598,7 @@ var record2 = (value) => value !== null && typeof value === "object" && !Array.i
|
|
|
13534
13598
|
var LOCAL_TOOLS = LOCAL_TOOL_NAMES;
|
|
13535
13599
|
var MAX_USER_STEERING = 10;
|
|
13536
13600
|
var CONTEXT_PRIORITY = "Latest user > chronological user steering > historical objective. Assistant never overrides user facts. Questions retain task.";
|
|
13601
|
+
var EARLIER = "[before later user messages] ";
|
|
13537
13602
|
var summarizeHarnessMessages = (messages, maxCharacters = 4000) => {
|
|
13538
13603
|
const redaction2 = createRedactionPolicy8({ includeEmails: true });
|
|
13539
13604
|
const clean = (text3, limit = 512) => redaction2.redactText(text3).replace(/\bBearer\s+\S+/gi, "Bearer [REDACTED]").replace(/\b([A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD)|api[_-]?key|access[_-]?token|password)\s*([=:])\s*(?:"[^"]*"|'[^']*'|\S+)/gi, "$1$2[REDACTED]").replace(/\b(?:sk|ghp|gho|github_pat)-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(/\s+/g, " ").trim().slice(0, limit);
|
|
@@ -13559,6 +13624,12 @@ var summarizeHarnessMessages = (messages, maxCharacters = 4000) => {
|
|
|
13559
13624
|
omitted = true;
|
|
13560
13625
|
}
|
|
13561
13626
|
};
|
|
13627
|
+
const markRecentEarlier = () => {
|
|
13628
|
+
const marked = recent.map((value) => value.startsWith(EARLIER) ? value : `${EARLIER}${value}`);
|
|
13629
|
+
recent.length = 0;
|
|
13630
|
+
for (const value of marked)
|
|
13631
|
+
add(recent, value, 4);
|
|
13632
|
+
};
|
|
13562
13633
|
const rememberObservation = (tool3, detail) => {
|
|
13563
13634
|
if (typeof tool3 !== "string" || !LOCAL_TOOLS.has(tool3) || typeof detail !== "string")
|
|
13564
13635
|
return;
|
|
@@ -13676,6 +13747,7 @@ var summarizeHarnessMessages = (messages, maxCharacters = 4000) => {
|
|
|
13676
13747
|
objective = text3;
|
|
13677
13748
|
if (historicalObjective)
|
|
13678
13749
|
add(steering, text3, MAX_USER_STEERING);
|
|
13750
|
+
markRecentEarlier();
|
|
13679
13751
|
} else
|
|
13680
13752
|
add(recent, `${message.role}: ${text3}`, 4);
|
|
13681
13753
|
} else if (part.type === "tool-call") {
|
|
@@ -15355,7 +15427,7 @@ import { z as z34 } from "zod";
|
|
|
15355
15427
|
// package.json
|
|
15356
15428
|
var package_default = {
|
|
15357
15429
|
name: "@zhivex-ai/harness",
|
|
15358
|
-
version: "1.
|
|
15430
|
+
version: "1.6.2",
|
|
15359
15431
|
description: "Governed, provider-portable runtime for coding agents with durable approvals and isolated repository execution.",
|
|
15360
15432
|
type: "module",
|
|
15361
15433
|
license: "MIT",
|
|
@@ -15455,6 +15527,7 @@ var package_default = {
|
|
|
15455
15527
|
"LICENSE",
|
|
15456
15528
|
"docs/CHANGE_ENVELOPES.md",
|
|
15457
15529
|
"docs/CLI.md",
|
|
15530
|
+
"docs/AUTO_APPROVE.md",
|
|
15458
15531
|
"docs/WEB_LIMITS.md",
|
|
15459
15532
|
"docs/CODE_RELEASE.md",
|
|
15460
15533
|
"docs/CODE_WEB_TASKS.md",
|
|
@@ -15571,26 +15644,26 @@ var package_default = {
|
|
|
15571
15644
|
"smoke:mcp:installed": "bun run build && bun scripts/mcp-installed-smoke.ts"
|
|
15572
15645
|
},
|
|
15573
15646
|
dependencies: {
|
|
15574
|
-
"@zhivex-ai/agents": "1.
|
|
15575
|
-
"@zhivex-ai/anthropic": "0.
|
|
15576
|
-
"@zhivex-ai/azure-openai": "0.
|
|
15577
|
-
"@zhivex-ai/bedrock": "2.0
|
|
15578
|
-
"@zhivex-ai/core": "1.
|
|
15579
|
-
"@zhivex-ai/gemini": "0.
|
|
15580
|
-
"@zhivex-ai/meta": "0.
|
|
15581
|
-
"@zhivex-ai/openai": "0.
|
|
15582
|
-
"@zhivex-ai/qwen": "0.
|
|
15583
|
-
"@zhivex-ai/vertex": "1.
|
|
15584
|
-
"google-auth-library": "11.1
|
|
15585
|
-
zod: "4.6.
|
|
15647
|
+
"@zhivex-ai/agents": "1.12.0",
|
|
15648
|
+
"@zhivex-ai/anthropic": "0.15.0",
|
|
15649
|
+
"@zhivex-ai/azure-openai": "0.8.0",
|
|
15650
|
+
"@zhivex-ai/bedrock": "2.1.0",
|
|
15651
|
+
"@zhivex-ai/core": "1.36.0",
|
|
15652
|
+
"@zhivex-ai/gemini": "0.14.0",
|
|
15653
|
+
"@zhivex-ai/meta": "0.3.0",
|
|
15654
|
+
"@zhivex-ai/openai": "0.17.0",
|
|
15655
|
+
"@zhivex-ai/qwen": "0.18.0",
|
|
15656
|
+
"@zhivex-ai/vertex": "1.3.0",
|
|
15657
|
+
"google-auth-library": "11.3.1",
|
|
15658
|
+
zod: "4.6.7"
|
|
15586
15659
|
},
|
|
15587
15660
|
overrides: {
|
|
15588
|
-
"@zhivex-ai/core": "1.
|
|
15661
|
+
"@zhivex-ai/core": "1.36.0"
|
|
15589
15662
|
},
|
|
15590
15663
|
devDependencies: {
|
|
15591
15664
|
"@agentclientprotocol/sdk": "1.8.0",
|
|
15592
15665
|
"@modelcontextprotocol/server": "2.3.1",
|
|
15593
|
-
"@types/bun": "1.4.
|
|
15666
|
+
"@types/bun": "1.4.3",
|
|
15594
15667
|
"@types/node": "^26.6.5",
|
|
15595
15668
|
typescript: "7.0.2",
|
|
15596
15669
|
"typescript-compiler-api": "npm:typescript@6.0.3"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface ControlHit {
|
|
2
|
+
line: number;
|
|
3
|
+
label: string;
|
|
4
|
+
}
|
|
5
|
+
/** C0 controls other than tab, LF, and CR, plus DEL and Unicode C1 controls. */
|
|
6
|
+
export declare const firstDisallowedControl: (text: string) => ControlHit | undefined;
|
|
7
|
+
/** Raw C0/DEL bytes. UTF-8 continuation bytes are left alone. */
|
|
8
|
+
export declare const firstDisallowedControlBytes: (bytes: Uint8Array) => ControlHit | undefined;
|
|
9
|
+
export declare const refuseControlMessage: (hit: ControlHit) => string;
|
|
10
|
+
export declare const binaryAfterEditMessage: (hit: ControlHit) => string;
|
|
11
|
+
/**
|
|
12
|
+
* Text edits refuse NUL and other control bytes unless the host passes
|
|
13
|
+
* allowControlBytes. That switch is not part of the model tool schema.
|
|
14
|
+
* afterWrite is the publication check; tests replace it to prove the rollback path.
|
|
15
|
+
*/
|
|
16
|
+
export declare const textGuards: {
|
|
17
|
+
beforeWrite(content: string, allowControlBytes: boolean): void;
|
|
18
|
+
afterWrite(bytes: Uint8Array, allowControlBytes: boolean): ControlHit | undefined;
|
|
19
|
+
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type ReplacementEdit } from "./replacement-edits.js";
|
|
2
2
|
import { type ApplyEditProposalInput, type ApplyPatchResult, type FileDigest, type MoveFileInput, type MoveFileResult, type MutationAuditEntry, type QuarantineFileInput, type QuarantineFileResult, type RestoreFileInput, type RestoreFileResult } from "./edit-contracts.js";
|
|
3
|
+
/** Host-only. Model tool input cannot set this, and the default is off. */
|
|
4
|
+
interface HostTextEditOptions {
|
|
5
|
+
allowControlBytes?: boolean;
|
|
6
|
+
}
|
|
3
7
|
export type HarnessCheck = string;
|
|
4
8
|
export interface WorkspaceFile {
|
|
5
9
|
path: string;
|
|
@@ -188,12 +192,12 @@ export declare class Workspace {
|
|
|
188
192
|
}[];
|
|
189
193
|
}>;
|
|
190
194
|
private replacementChanges;
|
|
191
|
-
applyReplacement(input: ReplacementEdit): Promise<ApplyPatchResult>;
|
|
192
|
-
applyPatch(input: ApplyEditProposalInput): Promise<ApplyPatchResult>;
|
|
195
|
+
applyReplacement(input: ReplacementEdit, host?: HostTextEditOptions): Promise<ApplyPatchResult>;
|
|
196
|
+
applyPatch(input: ApplyEditProposalInput, host?: HostTextEditOptions): Promise<ApplyPatchResult>;
|
|
193
197
|
applyPatchWithModes(input: ApplyEditProposalInput, modes: ReadonlyMap<string, {
|
|
194
198
|
beforeMode?: number;
|
|
195
199
|
afterMode: number;
|
|
196
|
-
}>, assertActive?: () => Promise<void
|
|
200
|
+
}>, assertActive?: () => Promise<void>, host?: HostTextEditOptions): Promise<ApplyPatchResult>;
|
|
197
201
|
private applyPatchWithModesLocked;
|
|
198
202
|
/** @deprecated Use applyPatch with an inspected expectedDigest. */
|
|
199
203
|
writeFile(relativePath: string, content: string, overwrite?: boolean): Promise<{
|
|
@@ -236,3 +240,4 @@ export declare class Workspace {
|
|
|
236
240
|
staged: CommandResult;
|
|
237
241
|
}>;
|
|
238
242
|
}
|
|
243
|
+
export {};
|
package/dist/zhx.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
HARNESS_VERSION,
|
|
4
4
|
NODE_ENGINE_RANGE,
|
|
5
5
|
shortCliHelp
|
|
6
|
-
} from "./cli-entry-
|
|
6
|
+
} from "./cli-entry-10th5awp.js";
|
|
7
7
|
import {
|
|
8
8
|
__require
|
|
9
9
|
} from "./cli-entry-1bx83sw4.js";
|
|
@@ -41,6 +41,6 @@ if (argv.length === 1 && ["--version", "-v", "version"].includes(argv[0])) {
|
|
|
41
41
|
process.stdout.write(`${shortCliHelp(HARNESS_VERSION)}
|
|
42
42
|
`);
|
|
43
43
|
} else {
|
|
44
|
-
const { runCli } = await import("./cli-
|
|
44
|
+
const { runCli } = await import("./cli-84en174d.js");
|
|
45
45
|
await runCli(argv);
|
|
46
46
|
}
|