@zhivex-ai/harness 1.4.1 → 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 +23 -0
- package/README.md +1 -1
- package/SUPPORT.md +1 -1
- package/contracts/stable-api-signatures.json +7 -7
- package/dist/acp-cli.js +10 -10
- package/dist/approvals/approval-diff.d.ts +2 -1
- package/dist/approvals/approval-preview.d.ts +1 -1
- package/dist/approvals/auto-approve.d.ts +137 -0
- package/dist/audit/index.js +6 -6
- package/dist/{audit-pack-e4vaq7np.js → audit-pack-fckpqfsb.js} +3 -3
- package/dist/{cli-1cf4dcnj.js → cli-84en174d.js} +14 -8
- package/dist/{cli-entry-3s63zn4w.js → cli-entry-10th5awp.js} +16 -15
- package/dist/{cli-entry-a890x3kq.js → cli-entry-4g2ewcnw.js} +15 -5
- package/dist/{cli-entry-7f0cen32.js → cli-entry-6amx4mcc.js} +209 -68
- package/dist/{cli-entry-rgs6bxmy.js → cli-entry-z1pbjrg4.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/compat/code-support.js +8 -8
- package/dist/engine/acp.js +11 -11
- package/dist/engine/client.js +10 -10
- package/dist/engine/desktop-state-v1.js +6 -6
- package/dist/engine/index.js +15 -15
- package/dist/engine/mcp-stdio-v1.js +5 -5
- package/dist/engine/service.js +11 -11
- package/dist/hostile-repository-demo.js +235 -84
- package/dist/{index-mv8sb1ns.js → index-1pn0ecs4.js} +55 -13
- package/dist/{index-dtb2c6ds.js → index-2r4f0p4n.js} +2 -2
- package/dist/{index-6599mpmy.js → index-4mn6m3bq.js} +3 -3
- package/dist/{index-xdbkcccf.js → index-6dxrbnxm.js} +2 -2
- package/dist/{index-crta5wzv.js → index-74f9yhfh.js} +4 -4
- package/dist/{index-f6mqa5gk.js → index-amjttjaj.js} +11 -39
- package/dist/{index-wm9xeq1n.js → index-bt2p8x7r.js} +1 -1
- package/dist/{index-sk3cap35.js → index-eps3c3g6.js} +1 -1
- package/dist/{index-zxg4ve4j.js → index-fqqz1yt8.js} +77 -4
- package/dist/{index-02pqtbbg.js → index-paf7z8kn.js} +10 -10
- package/dist/{index-0zs2dz26.js → index-pf73y4gt.js} +18 -9
- package/dist/{index-74kkpyqk.js → index-rgqzdgv6.js} +12 -6
- package/dist/{index-4fx5f3k5.js → index-rz7wmp71.js} +16 -15
- package/dist/{index-d9zkmhz3.js → index-w0m1nckc.js} +127 -28
- package/dist/{index-sstgcsr0.js → index-yx52bt4a.js} +1 -1
- package/dist/{index-j6zxnc8m.js → index-zrz4rs7p.js} +5 -5
- package/dist/index.js +14 -14
- package/dist/integrations/mcp-stdio-tools.d.ts +4 -1
- package/dist/providers/bedrock.d.ts +1 -1
- package/dist/review/reveal-invisible.d.ts +13 -0
- package/dist/runtime/budget-ledger-scope.d.ts +14 -0
- package/dist/service-cli.js +11 -11
- package/dist/{service-client-cli-eqrdc9zq.js → service-client-cli-pp5z4dmf.js} +4 -4
- package/dist/tools/workspace.d.ts +2 -2
- package/dist/verify-historical-migrations.js +240 -86
- package/dist/workspace/edit-contracts.d.ts +2 -2
- package/dist/workspace/path-policy.d.ts +2 -1
- package/dist/workspace/text-integrity.d.ts +19 -0
- package/dist/workspace/workspace.d.ts +21 -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 +36 -6
- package/docs/CODE_WEB_TASKS.md +1 -1
- package/docs/CREDENTIALS.md +1 -1
- package/docs/DURABLE_OPERATIONS.md +1 -1
- package/docs/ENGINE_API.md +1 -1
- package/docs/EXTENSIBILITY.md +7 -3
- package/docs/FIRST_USE.md +6 -6
- package/docs/MINIMUM_TASK_AUTHORITY.md +5 -5
- package/docs/PROJECT_MEMORY.md +1 -1
- package/docs/PUBLIC_SECURITY.md +14 -1
- package/docs/REPOSITORY_EDITING.md +1 -1
- package/docs/TOOL_POLICY.md +7 -1
- package/docs/VERTEX.md +2 -2
- package/docs/WEB_LIMITS.md +2 -2
- package/docs/adr/0002-isolated-mcp-stdio.md +2 -2
- package/evaluations/representative-assembly-matrix.json +37 -1
- package/examples/README.md +3 -3
- package/package.json +16 -15
|
@@ -5567,8 +5567,8 @@ var quarantineFileInputSchema = z11.strictObject({
|
|
|
5567
5567
|
});
|
|
5568
5568
|
var restoreFileInputSchema = z11.strictObject({
|
|
5569
5569
|
quarantineId: z11.string().min(1).max(200).regex(/^[A-Za-z0-9._:-]+$/),
|
|
5570
|
-
destination: workspaceFilePathSchema.
|
|
5571
|
-
expectedDigest: fileDigestSchema.
|
|
5570
|
+
destination: workspaceFilePathSchema.describe("Required workspace path for the quarantined bytes. Restore does not fall back to the original path."),
|
|
5571
|
+
expectedDigest: fileDigestSchema.describe("Required sha256 of the quarantined bytes the operator reviewed.")
|
|
5572
5572
|
});
|
|
5573
5573
|
var mutationOperationSchema = z11.enum(["create", "update", "move", "quarantine", "restore"]);
|
|
5574
5574
|
var mutationAuditEntrySchema = z11.strictObject({
|
|
@@ -5905,13 +5905,62 @@ 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) => {
|
|
5911
|
-
const normalized = name.
|
|
5960
|
+
const normalized = name.toLowerCase();
|
|
5912
5961
|
return normalized === ".env" || normalized.startsWith(".env.") && normalized !== ".env.example" || normalized === ".npmrc" || normalized === "id_rsa" || normalized === "id_ed25519" || [".key", ".pem", ".p12", ".pfx"].some((extension) => normalized.endsWith(extension));
|
|
5913
5962
|
};
|
|
5914
|
-
var isHardIgnored = (relativePath) => relativePath.split("/").some((segment) => HARD_IGNORES.has(segment.
|
|
5963
|
+
var isHardIgnored = (relativePath) => relativePath.split("/").some((segment) => HARD_IGNORES.has(segment.toLowerCase()) || isSensitiveName(segment));
|
|
5915
5964
|
|
|
5916
5965
|
// src/workspace/workspace.ts
|
|
5917
5966
|
var IGNORE_FILES = [".gitignore", ".zhivex-harnessignore"];
|
|
@@ -6191,7 +6240,7 @@ class Workspace {
|
|
|
6191
6240
|
throw new HarnessWorkspaceError(`The path escapes the workspace: ${relativePath}`);
|
|
6192
6241
|
const relative = wirePath(path11.relative(this.root, candidate));
|
|
6193
6242
|
if (relative && isHardIgnored(relative)) {
|
|
6194
|
-
const blocked = relative.split("/").find((segment) => HARD_IGNORES.has(segment.
|
|
6243
|
+
const blocked = relative.split("/").find((segment) => HARD_IGNORES.has(segment.toLowerCase()) || isSensitiveName(segment));
|
|
6195
6244
|
throw new HarnessWorkspaceError(`The path is protected by the harness policy: ${blocked}`);
|
|
6196
6245
|
}
|
|
6197
6246
|
return candidate;
|
|
@@ -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) {
|
|
@@ -7174,30 +7238,65 @@ class Workspace {
|
|
|
7174
7238
|
const audit = await this.audit({ operation: "quarantine", path: parsed.path, beforeDigest: source.digest, quarantineId });
|
|
7175
7239
|
return { quarantineId, path: parsed.path, digest: source.digest, audit };
|
|
7176
7240
|
}
|
|
7177
|
-
async
|
|
7178
|
-
return withWorkspaceMutation(this.root, () => this.restoreQuarantinedLocked(input));
|
|
7179
|
-
}
|
|
7180
|
-
async restoreQuarantinedLocked(input) {
|
|
7181
|
-
const parsed = restoreFileInputSchema.parse(input);
|
|
7182
|
-
this.checkAuditCapacity();
|
|
7241
|
+
async readRestorableQuarantine(quarantineId) {
|
|
7183
7242
|
const directory = await this.secureQuarantineDirectory(false);
|
|
7184
7243
|
let manifest;
|
|
7185
7244
|
try {
|
|
7186
|
-
const manifestFile = await this.readInternalRegularFile(path11.join(directory, `${
|
|
7245
|
+
const manifestFile = await this.readInternalRegularFile(path11.join(directory, `${quarantineId}.json`), "Quarantine manifest", 64 * 1024);
|
|
7187
7246
|
manifest = JSON.parse(manifestFile.contents.toString("utf8"));
|
|
7188
7247
|
} catch {
|
|
7189
|
-
throw new HarnessWorkspaceError(`Quarantine entry was not found: ${
|
|
7248
|
+
throw new HarnessWorkspaceError(`Quarantine entry was not found: ${quarantineId}.`);
|
|
7190
7249
|
}
|
|
7191
|
-
if (manifest.schemaVersion !== 1 || manifest.quarantineId !==
|
|
7192
|
-
throw new HarnessWorkspaceError(`Quarantine entry is not restorable: ${
|
|
7250
|
+
if (manifest.schemaVersion !== 1 || manifest.quarantineId !== quarantineId || manifest.status !== "quarantined") {
|
|
7251
|
+
throw new HarnessWorkspaceError(`Quarantine entry is not restorable: ${quarantineId}.`);
|
|
7193
7252
|
}
|
|
7194
|
-
const dataPath = path11.join(directory, `${
|
|
7253
|
+
const dataPath = path11.join(directory, `${quarantineId}.data`);
|
|
7195
7254
|
const dataFile = await this.readInternalRegularFile(dataPath, "Quarantine payload", MAX_FILE_BYTES);
|
|
7196
7255
|
const digest3 = digestBytes(dataFile.contents);
|
|
7197
|
-
if (digest3 !== manifest.digest
|
|
7256
|
+
if (digest3 !== manifest.digest) {
|
|
7257
|
+
throw new HarnessWorkspaceError(`Quarantine digest mismatch: ${quarantineId}.`);
|
|
7258
|
+
}
|
|
7259
|
+
return { manifest, dataPath, dataFile, digest: digest3 };
|
|
7260
|
+
}
|
|
7261
|
+
async previewRestore(input) {
|
|
7262
|
+
const parsed = restoreFileInputSchema.parse(input);
|
|
7263
|
+
const quarantined = await this.readRestorableQuarantine(parsed.quarantineId);
|
|
7264
|
+
if (parsed.expectedDigest !== quarantined.digest) {
|
|
7198
7265
|
throw new HarnessWorkspaceError(`Quarantine digest mismatch: ${parsed.quarantineId}.`);
|
|
7199
7266
|
}
|
|
7200
|
-
const
|
|
7267
|
+
const destination = await this.safePath(parsed.destination, { allowMissing: true });
|
|
7268
|
+
if (destination.exists)
|
|
7269
|
+
throw new HarnessWorkspaceError(`Restore destination already exists: ${parsed.destination}.`);
|
|
7270
|
+
let after;
|
|
7271
|
+
try {
|
|
7272
|
+
after = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(quarantined.dataFile.contents);
|
|
7273
|
+
} catch {
|
|
7274
|
+
throw new HarnessWorkspaceError("Preview requires valid UTF-8 text.");
|
|
7275
|
+
}
|
|
7276
|
+
return {
|
|
7277
|
+
proposalId: parsed.expectedDigest,
|
|
7278
|
+
files: [{
|
|
7279
|
+
path: parsed.destination,
|
|
7280
|
+
expectedDigest: parsed.expectedDigest,
|
|
7281
|
+
before: null,
|
|
7282
|
+
after,
|
|
7283
|
+
afterDigest: quarantined.digest,
|
|
7284
|
+
operation: "restore"
|
|
7285
|
+
}]
|
|
7286
|
+
};
|
|
7287
|
+
}
|
|
7288
|
+
async restoreQuarantined(input) {
|
|
7289
|
+
return withWorkspaceMutation(this.root, () => this.restoreQuarantinedLocked(input));
|
|
7290
|
+
}
|
|
7291
|
+
async restoreQuarantinedLocked(input) {
|
|
7292
|
+
const parsed = restoreFileInputSchema.parse(input);
|
|
7293
|
+
this.checkAuditCapacity();
|
|
7294
|
+
const quarantined = await this.readRestorableQuarantine(parsed.quarantineId);
|
|
7295
|
+
if (parsed.expectedDigest !== quarantined.digest) {
|
|
7296
|
+
throw new HarnessWorkspaceError(`Quarantine digest mismatch: ${parsed.quarantineId}.`);
|
|
7297
|
+
}
|
|
7298
|
+
const { manifest, dataPath, dataFile, digest: digest3 } = quarantined;
|
|
7299
|
+
const destinationPath = parsed.destination;
|
|
7201
7300
|
const destination = await this.safePath(destinationPath, { allowMissing: true });
|
|
7202
7301
|
if (destination.exists)
|
|
7203
7302
|
throw new HarnessWorkspaceError(`Restore destination already exists: ${destinationPath}.`);
|
|
@@ -8420,7 +8519,7 @@ var importPatch = async (runId, expectedPatchId, host, base, current2, maxFileWr
|
|
|
8420
8519
|
const rollbackErrors = [];
|
|
8421
8520
|
for (const item of [...quarantined].reverse()) {
|
|
8422
8521
|
try {
|
|
8423
|
-
await host.restoreQuarantined({ quarantineId: item.quarantineId, expectedDigest: item.digest });
|
|
8522
|
+
await host.restoreQuarantined({ quarantineId: item.quarantineId, destination: item.path, expectedDigest: item.digest });
|
|
8424
8523
|
} catch (rollbackError) {
|
|
8425
8524
|
rollbackErrors.push(rollbackError);
|
|
8426
8525
|
}
|
|
@@ -9718,6 +9817,79 @@ import { createAgentRunLedger } from "@zhivex-ai/agents/control-plane";
|
|
|
9718
9817
|
import { AsyncLocalStorage as AsyncLocalStorage5 } from "node:async_hooks";
|
|
9719
9818
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
9720
9819
|
import { z as z18 } from "zod";
|
|
9820
|
+
|
|
9821
|
+
// src/runtime/budget-ledger-scope.ts
|
|
9822
|
+
var BUDGET_LEDGER_NAMESPACE = "__zhivex_budget__";
|
|
9823
|
+
var BUDGET_OWNER_NAMESPACE_KEY = "budgetOwnerNamespace";
|
|
9824
|
+
var isReservedBudgetNamespace = (namespace) => namespace === BUDGET_LEDGER_NAMESPACE || (namespace?.startsWith(`${BUDGET_LEDGER_NAMESPACE}:`) ?? false);
|
|
9825
|
+
var budgetLedgerNamespace = (namespace) => {
|
|
9826
|
+
if (!namespace || isReservedBudgetNamespace(namespace)) {
|
|
9827
|
+
throw new HarnessStateConflictError("The budget ledger namespace is reserved.");
|
|
9828
|
+
}
|
|
9829
|
+
return `${BUDGET_LEDGER_NAMESPACE}:${namespace}`;
|
|
9830
|
+
};
|
|
9831
|
+
var budgetLedgerScope = (scope) => {
|
|
9832
|
+
if (scope.namespace && isReservedBudgetNamespace(scope.namespace)) {
|
|
9833
|
+
throw new HarnessStateConflictError("The budget ledger namespace is reserved.");
|
|
9834
|
+
}
|
|
9835
|
+
return {
|
|
9836
|
+
tenantId: scope.tenantId,
|
|
9837
|
+
...scope.userId ? { userId: scope.userId } : {},
|
|
9838
|
+
namespace: scope.namespace ? budgetLedgerNamespace(scope.namespace) : `${BUDGET_LEDGER_NAMESPACE}:`
|
|
9839
|
+
};
|
|
9840
|
+
};
|
|
9841
|
+
var sameIdentity = (left, right) => left?.tenantId === right.tenantId && (left.userId ?? undefined) === (right.userId ?? undefined);
|
|
9842
|
+
var bindBudgetLedgerStore = (primary, scope, budget = primary) => {
|
|
9843
|
+
const bound = budgetLedgerScope(scope);
|
|
9844
|
+
const ownerNamespace = scope.namespace ?? "";
|
|
9845
|
+
const isSdkBudget = (value) => sameIdentity(value, bound) && value?.namespace === BUDGET_LEDGER_NAMESPACE;
|
|
9846
|
+
const isBoundBudget = (value) => sameIdentity(value, bound) && value?.namespace === bound.namespace;
|
|
9847
|
+
const assertId = (id) => {
|
|
9848
|
+
if (!/^budget_[a-f0-9]{64}$/.test(id))
|
|
9849
|
+
throw new HarnessStateConflictError("Invalid budget ledger identity.");
|
|
9850
|
+
};
|
|
9851
|
+
const bindState = (state) => {
|
|
9852
|
+
const metadata = { ...state.metadata ?? {} };
|
|
9853
|
+
const owner = metadata[BUDGET_OWNER_NAMESPACE_KEY];
|
|
9854
|
+
if (owner !== undefined && owner !== ownerNamespace) {
|
|
9855
|
+
throw new HarnessStateConflictError("Budget ledger is bound to another namespace.");
|
|
9856
|
+
}
|
|
9857
|
+
return {
|
|
9858
|
+
...state,
|
|
9859
|
+
scope: bound,
|
|
9860
|
+
metadata: { ...metadata, [BUDGET_OWNER_NAMESPACE_KEY]: ownerNamespace }
|
|
9861
|
+
};
|
|
9862
|
+
};
|
|
9863
|
+
const assertStored = (state) => {
|
|
9864
|
+
if (!state)
|
|
9865
|
+
return state;
|
|
9866
|
+
const owner = state.metadata?.[BUDGET_OWNER_NAMESPACE_KEY];
|
|
9867
|
+
if (state.scope?.namespace !== bound.namespace || owner !== ownerNamespace) {
|
|
9868
|
+
throw new HarnessStateConflictError("Budget ledger is not bound to this namespace.");
|
|
9869
|
+
}
|
|
9870
|
+
return state;
|
|
9871
|
+
};
|
|
9872
|
+
return {
|
|
9873
|
+
...primary,
|
|
9874
|
+
load: async (id, requestedScope) => {
|
|
9875
|
+
if (!isSdkBudget(requestedScope) && !isBoundBudget(requestedScope))
|
|
9876
|
+
return primary.load(id, requestedScope);
|
|
9877
|
+
assertId(id);
|
|
9878
|
+
return assertStored(await budget.load(id, bound));
|
|
9879
|
+
},
|
|
9880
|
+
save: async (state, options) => {
|
|
9881
|
+
if (!isSdkBudget(state.scope) && !isBoundBudget(state.scope))
|
|
9882
|
+
return primary.save(state, options);
|
|
9883
|
+
assertId(state.runId);
|
|
9884
|
+
if (state.provider !== "zhivex" || state.modelId !== "budget-coordinator" || state.metadata?.budgetCoordinator !== true) {
|
|
9885
|
+
throw new HarnessStateConflictError("Only SDK budget ledger states may use the reserved namespace.");
|
|
9886
|
+
}
|
|
9887
|
+
return budget.save(bindState(state), options);
|
|
9888
|
+
}
|
|
9889
|
+
};
|
|
9890
|
+
};
|
|
9891
|
+
|
|
9892
|
+
// src/runtime/task-budget.ts
|
|
9721
9893
|
import {
|
|
9722
9894
|
createAgentBudgetCoordinator,
|
|
9723
9895
|
fingerprintAgentHarness as fingerprintAgentHarness2,
|
|
@@ -9836,10 +10008,10 @@ class TaskBudget {
|
|
|
9836
10008
|
this.accountRunId = TaskBudget.accountId(scope, taskId);
|
|
9837
10009
|
this.policy = structuredClone(account.policy);
|
|
9838
10010
|
const budgetId = this.accountRunId;
|
|
9839
|
-
this.coordinator = createAgentBudgetCoordinator({ store, scope, budgetId, limits: this.policy.limits });
|
|
10011
|
+
this.coordinator = createAgentBudgetCoordinator({ store: bindBudgetLedgerStore(store, scope), scope, budgetId, limits: this.policy.limits });
|
|
9840
10012
|
this.coordinatorId = this.coordinator.id;
|
|
9841
10013
|
this.budgetRunId = `budget_${fingerprintAgentHarness2({ budgetId, scope }).slice("sha256:".length)}`;
|
|
9842
|
-
this.budgetScope =
|
|
10014
|
+
this.budgetScope = budgetLedgerScope(scope);
|
|
9843
10015
|
}
|
|
9844
10016
|
static accountId(scope, taskId) {
|
|
9845
10017
|
return `task_budget_${fingerprintAgentHarness2({ scope, taskId }).slice("sha256:".length)}`;
|
|
@@ -10354,37 +10526,7 @@ class TaskBudget {
|
|
|
10354
10526
|
}
|
|
10355
10527
|
|
|
10356
10528
|
// src/persistence/operations.ts
|
|
10357
|
-
var
|
|
10358
|
-
tenantId: config.scope.tenantId,
|
|
10359
|
-
...config.scope.userId ? { userId: config.scope.userId } : {},
|
|
10360
|
-
namespace: "__zhivex_budget__"
|
|
10361
|
-
});
|
|
10362
|
-
var withBudgetLedgerStore = (primary, budget, config) => {
|
|
10363
|
-
const scope = budgetScopeFor(config);
|
|
10364
|
-
const budgetScope = (value) => value?.namespace === scope.namespace && value.tenantId === scope.tenantId && (value.userId ?? undefined) === (scope.userId ?? undefined);
|
|
10365
|
-
const assertId = (id) => {
|
|
10366
|
-
if (!/^budget_[a-f0-9]{64}$/.test(id))
|
|
10367
|
-
throw new HarnessStateConflictError("Invalid budget ledger identity.");
|
|
10368
|
-
};
|
|
10369
|
-
return {
|
|
10370
|
-
...primary,
|
|
10371
|
-
load: async (id, requestedScope) => {
|
|
10372
|
-
if (!budgetScope(requestedScope))
|
|
10373
|
-
return primary.load(id, requestedScope);
|
|
10374
|
-
assertId(id);
|
|
10375
|
-
return budget.load(id, requestedScope);
|
|
10376
|
-
},
|
|
10377
|
-
save: async (state, options) => {
|
|
10378
|
-
if (!budgetScope(state.scope))
|
|
10379
|
-
return primary.save(state, options);
|
|
10380
|
-
assertId(state.runId);
|
|
10381
|
-
if (state.provider !== "zhivex" || state.modelId !== "budget-coordinator" || state.metadata?.budgetCoordinator !== true) {
|
|
10382
|
-
throw new HarnessStateConflictError("Only SDK budget ledger states may use the reserved namespace.");
|
|
10383
|
-
}
|
|
10384
|
-
return budget.save(state, options);
|
|
10385
|
-
}
|
|
10386
|
-
};
|
|
10387
|
-
};
|
|
10529
|
+
var withBudgetLedgerStore = (primary, budget, config) => bindBudgetLedgerStore(primary, config.scope, budget);
|
|
10388
10530
|
var HARNESS_SQLITE_FILE = "operations.sqlite";
|
|
10389
10531
|
var HARNESS_OPERATIONS_SCHEMA_VERSION = 1;
|
|
10390
10532
|
var runSummary = (state) => {
|
|
@@ -10555,13 +10697,12 @@ var migrateLegacyFileRuns = async (directory, target, config) => {
|
|
|
10555
10697
|
return result;
|
|
10556
10698
|
};
|
|
10557
10699
|
var openHarnessPersistence = async (config, options = {}) => {
|
|
10558
|
-
|
|
10559
|
-
throw new HarnessStateConflictError("The budget ledger namespace is reserved.");
|
|
10700
|
+
const budgetScope = budgetLedgerScope(config.scope);
|
|
10560
10701
|
await validateStateDirectory(config.workspace, config.stateDirectory);
|
|
10561
10702
|
await ensurePrivateStateDirectory(config.stateDirectory);
|
|
10562
10703
|
await protectStateFromGit(config.workspace, config.stateDirectory);
|
|
10563
10704
|
if (config.storeBackend === "file") {
|
|
10564
|
-
const store = withBudgetLedgerStore(createFileAgentRunStore({ directory: config.stateDirectory, scope: config.scope }), createFileAgentRunStore({ directory: config.stateDirectory, scope:
|
|
10705
|
+
const store = withBudgetLedgerStore(createFileAgentRunStore({ directory: config.stateDirectory, scope: config.scope }), createFileAgentRunStore({ directory: config.stateDirectory, scope: budgetScope }), config);
|
|
10565
10706
|
const migration = shouldMigrateLegacyRuns(config, options.migrateLegacyFileStore) ? await migrateLegacyFileRuns(config.stateDirectory, store, config) : emptyMigration();
|
|
10566
10707
|
return {
|
|
10567
10708
|
backend: "file",
|
|
@@ -10606,7 +10747,7 @@ var openHarnessPersistence = async (config, options = {}) => {
|
|
|
10606
10747
|
database.exec("PRAGMA foreign_keys = ON");
|
|
10607
10748
|
await chmod3(databasePath, 384);
|
|
10608
10749
|
const databaseLike = sqliteAdapter(database);
|
|
10609
|
-
const store = withBudgetLedgerStore(createSqliteAgentRunStore({ db: databaseLike, scope: config.scope, history: "incremental" }), createSqliteAgentRunStore({ db: databaseLike, scope:
|
|
10750
|
+
const store = withBudgetLedgerStore(createSqliteAgentRunStore({ db: databaseLike, scope: config.scope, history: "incremental" }), createSqliteAgentRunStore({ db: databaseLike, scope: budgetScope }), config);
|
|
10610
10751
|
database.exec(`CREATE TRIGGER IF NOT EXISTS zhivex_task_budget_retain_run
|
|
10611
10752
|
BEFORE DELETE ON zhivex_agent_runs
|
|
10612
10753
|
WHEN json_type(OLD.state_json, '$.metadata.zhivexTaskBudgetAccountV1') IS NOT NULL
|
|
@@ -10774,7 +10915,7 @@ var projectMemoryBinding = (workspace, scope) => {
|
|
|
10774
10915
|
segment(scope.userId);
|
|
10775
10916
|
if (scope.namespace !== undefined)
|
|
10776
10917
|
segment(scope.namespace);
|
|
10777
|
-
if (scope.namespace
|
|
10918
|
+
if (isReservedBudgetNamespace(scope.namespace))
|
|
10778
10919
|
throw new HarnessConfigError("Reserved memory namespace.");
|
|
10779
10920
|
const key = (kind, value) => createHash15("sha256").update(kind).update("\x00").update(value).digest("hex");
|
|
10780
10921
|
const workspaceKey = key("workspace", workspace);
|
|
@@ -11166,7 +11307,8 @@ var coordinatorIds = (runs) => new Set(runs.flatMap(({ state }) => {
|
|
|
11166
11307
|
var validateBudgetLedger = (config, run, identities) => {
|
|
11167
11308
|
const state = validatedRunState(run.state, `Budget ledger ${run.key}`);
|
|
11168
11309
|
const identity = state.metadata?.budgetIdentity;
|
|
11169
|
-
|
|
11310
|
+
const ownerNamespace = config.scope.namespace;
|
|
11311
|
+
if (!ownerNamespace || state.scope?.namespace !== budgetLedgerNamespace(ownerNamespace) || state.metadata?.[BUDGET_OWNER_NAMESPACE_KEY] !== ownerNamespace || state.scope?.tenantId !== config.scope.tenantId || (state.scope?.userId ?? undefined) !== (config.scope.userId ?? undefined) || state.provider !== "zhivex" || state.modelId !== "budget-coordinator" || state.metadata?.budgetCoordinator !== true || typeof identity !== "string" || !identities.has(identity) || !/^budget_[a-f0-9]{64}$/.test(state.runId) || !sdkRunKeyMatches(run.key, state.scope, state.runId)) {
|
|
11170
11312
|
throw new HarnessStateConflictError("Budget ledger is not bound to an exported run and durable scope.");
|
|
11171
11313
|
}
|
|
11172
11314
|
assertTerminalState(state, `Budget ledger ${run.key}`);
|
|
@@ -11300,7 +11442,9 @@ var readPayload = async (config, recordedWorkspace, accessLease) => {
|
|
|
11300
11442
|
const metadata = state.metadata;
|
|
11301
11443
|
if (typeof metadata?.budgetIdentity !== "string" || !identities.has(metadata.budgetIdentity)) {
|
|
11302
11444
|
const scope = state.scope;
|
|
11303
|
-
|
|
11445
|
+
const sameTenant = scope?.tenantId === config.scope.tenantId && (scope?.userId ?? undefined) === (config.scope.userId ?? undefined);
|
|
11446
|
+
const boundNamespace = config.scope.namespace ? budgetLedgerNamespace(config.scope.namespace) : undefined;
|
|
11447
|
+
if (sameTenant && (scope?.namespace === BUDGET_LEDGER_NAMESPACE || scope?.namespace === boundNamespace)) {
|
|
11304
11448
|
throw new HarnessStateConflictError("An unlinked shared budget ledger cannot be safely assigned to this backup scope; retain the original database.");
|
|
11305
11449
|
}
|
|
11306
11450
|
return [];
|
|
@@ -13454,6 +13598,7 @@ var record2 = (value) => value !== null && typeof value === "object" && !Array.i
|
|
|
13454
13598
|
var LOCAL_TOOLS = LOCAL_TOOL_NAMES;
|
|
13455
13599
|
var MAX_USER_STEERING = 10;
|
|
13456
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] ";
|
|
13457
13602
|
var summarizeHarnessMessages = (messages, maxCharacters = 4000) => {
|
|
13458
13603
|
const redaction2 = createRedactionPolicy8({ includeEmails: true });
|
|
13459
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);
|
|
@@ -13479,6 +13624,12 @@ var summarizeHarnessMessages = (messages, maxCharacters = 4000) => {
|
|
|
13479
13624
|
omitted = true;
|
|
13480
13625
|
}
|
|
13481
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
|
+
};
|
|
13482
13633
|
const rememberObservation = (tool3, detail) => {
|
|
13483
13634
|
if (typeof tool3 !== "string" || !LOCAL_TOOLS.has(tool3) || typeof detail !== "string")
|
|
13484
13635
|
return;
|
|
@@ -13596,6 +13747,7 @@ var summarizeHarnessMessages = (messages, maxCharacters = 4000) => {
|
|
|
13596
13747
|
objective = text3;
|
|
13597
13748
|
if (historicalObjective)
|
|
13598
13749
|
add(steering, text3, MAX_USER_STEERING);
|
|
13750
|
+
markRecentEarlier();
|
|
13599
13751
|
} else
|
|
13600
13752
|
add(recent, `${message.role}: ${text3}`, 4);
|
|
13601
13753
|
} else if (part.type === "tool-call") {
|
|
@@ -15140,7 +15292,8 @@ var resolvers = {
|
|
|
15140
15292
|
const parsed = moveFileInputSchema.parse(input);
|
|
15141
15293
|
return [parsed.source, parsed.destination];
|
|
15142
15294
|
},
|
|
15143
|
-
quarantine_file: (input) => [quarantineFileInputSchema.parse(input).path]
|
|
15295
|
+
quarantine_file: (input) => [quarantineFileInputSchema.parse(input).path],
|
|
15296
|
+
restore_file: (input) => [restoreFileInputSchema.parse(input).destination]
|
|
15144
15297
|
};
|
|
15145
15298
|
var validateBuiltinToolPolicy = (policy, tools) => {
|
|
15146
15299
|
for (const rule of policy.rules)
|
|
@@ -15274,7 +15427,7 @@ import { z as z34 } from "zod";
|
|
|
15274
15427
|
// package.json
|
|
15275
15428
|
var package_default = {
|
|
15276
15429
|
name: "@zhivex-ai/harness",
|
|
15277
|
-
version: "1.
|
|
15430
|
+
version: "1.6.2",
|
|
15278
15431
|
description: "Governed, provider-portable runtime for coding agents with durable approvals and isolated repository execution.",
|
|
15279
15432
|
type: "module",
|
|
15280
15433
|
license: "MIT",
|
|
@@ -15374,6 +15527,7 @@ var package_default = {
|
|
|
15374
15527
|
"LICENSE",
|
|
15375
15528
|
"docs/CHANGE_ENVELOPES.md",
|
|
15376
15529
|
"docs/CLI.md",
|
|
15530
|
+
"docs/AUTO_APPROVE.md",
|
|
15377
15531
|
"docs/WEB_LIMITS.md",
|
|
15378
15532
|
"docs/CODE_RELEASE.md",
|
|
15379
15533
|
"docs/CODE_WEB_TASKS.md",
|
|
@@ -15490,26 +15644,26 @@ var package_default = {
|
|
|
15490
15644
|
"smoke:mcp:installed": "bun run build && bun scripts/mcp-installed-smoke.ts"
|
|
15491
15645
|
},
|
|
15492
15646
|
dependencies: {
|
|
15493
|
-
"@zhivex-ai/agents": "1.
|
|
15494
|
-
"@zhivex-ai/anthropic": "0.
|
|
15495
|
-
"@zhivex-ai/azure-openai": "0.
|
|
15496
|
-
"@zhivex-ai/bedrock": "2.0
|
|
15497
|
-
"@zhivex-ai/core": "1.
|
|
15498
|
-
"@zhivex-ai/gemini": "0.
|
|
15499
|
-
"@zhivex-ai/meta": "0.
|
|
15500
|
-
"@zhivex-ai/openai": "0.
|
|
15501
|
-
"@zhivex-ai/qwen": "0.
|
|
15502
|
-
"@zhivex-ai/vertex": "1.
|
|
15503
|
-
"google-auth-library": "11.1
|
|
15504
|
-
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"
|
|
15505
15659
|
},
|
|
15506
15660
|
overrides: {
|
|
15507
|
-
"@zhivex-ai/core": "1.
|
|
15661
|
+
"@zhivex-ai/core": "1.36.0"
|
|
15508
15662
|
},
|
|
15509
15663
|
devDependencies: {
|
|
15510
15664
|
"@agentclientprotocol/sdk": "1.8.0",
|
|
15511
15665
|
"@modelcontextprotocol/server": "2.3.1",
|
|
15512
|
-
"@types/bun": "1.4.
|
|
15666
|
+
"@types/bun": "1.4.3",
|
|
15513
15667
|
"@types/node": "^26.6.5",
|
|
15514
15668
|
typescript: "7.0.2",
|
|
15515
15669
|
"typescript-compiler-api": "npm:typescript@6.0.3"
|
|
@@ -16799,7 +16953,7 @@ var createWorkspaceTools = (workspace, allowedChecks) => ({
|
|
|
16799
16953
|
}),
|
|
16800
16954
|
restore_file: tool5({
|
|
16801
16955
|
name: "restore_file",
|
|
16802
|
-
description: "Restore a quarantined file to
|
|
16956
|
+
description: "Restore a quarantined file to the required destination after the reviewed expectedDigest matches the quarantined bytes. Restore does not fall back to the original path and never overwrites an existing file.",
|
|
16803
16957
|
schema: restoreFileInputSchema,
|
|
16804
16958
|
...mutationApproval,
|
|
16805
16959
|
execute: async (input, context) => serializeJsonValue9(editContractDocument("restore-result", await (harnessExecutionSession(context)?.workspace ?? workspace).restoreQuarantined(input)))
|
|
@@ -70,8 +70,8 @@ export declare const quarantineFileInputSchema: z.ZodObject<{
|
|
|
70
70
|
export type QuarantineFileInput = z.infer<typeof quarantineFileInputSchema>;
|
|
71
71
|
export declare const restoreFileInputSchema: z.ZodObject<{
|
|
72
72
|
quarantineId: z.ZodString;
|
|
73
|
-
destination: z.
|
|
74
|
-
expectedDigest: z.
|
|
73
|
+
destination: z.ZodString;
|
|
74
|
+
expectedDigest: z.ZodString;
|
|
75
75
|
}, z.core.$strict>;
|
|
76
76
|
export type RestoreFileInput = z.infer<typeof restoreFileInputSchema>;
|
|
77
77
|
export declare const mutationOperationSchema: z.ZodEnum<{
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/** Shared lexical exclusions for workspace access and acceptance preflight.
|
|
1
|
+
/** Shared lexical exclusions for workspace access and acceptance preflight.
|
|
2
|
+
* Comparisons use toLowerCase so a Turkish locale cannot map ASCII I to dotless ı and skip .git or id_rsa. */
|
|
2
3
|
export declare const HARD_IGNORES: Set<string>;
|
|
3
4
|
export declare const isSensitiveName: (name: string) => boolean;
|
|
4
5
|
export declare const isHardIgnored: (relativePath: string) => boolean;
|
|
@@ -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<{
|
|
@@ -214,6 +218,19 @@ export declare class Workspace {
|
|
|
214
218
|
private readInternalRegularFile;
|
|
215
219
|
quarantineFile(input: QuarantineFileInput, assertActive?: () => Promise<void>): Promise<QuarantineFileResult>;
|
|
216
220
|
private quarantineFileLocked;
|
|
221
|
+
private readRestorableQuarantine;
|
|
222
|
+
/** Read-only preview of a pinned restore. Destination and digest are required, and nothing is written. */
|
|
223
|
+
previewRestore(input: unknown): Promise<{
|
|
224
|
+
proposalId: string;
|
|
225
|
+
files: {
|
|
226
|
+
path: string;
|
|
227
|
+
expectedDigest: string;
|
|
228
|
+
before: null;
|
|
229
|
+
after: string;
|
|
230
|
+
afterDigest: string;
|
|
231
|
+
operation: "restore";
|
|
232
|
+
}[];
|
|
233
|
+
}>;
|
|
217
234
|
restoreQuarantined(input: RestoreFileInput): Promise<RestoreFileResult>;
|
|
218
235
|
private restoreQuarantinedLocked;
|
|
219
236
|
runCheck(check: HarnessCheck, expectedScript: string, allowedChecks?: readonly string[]): Promise<CommandResult>;
|
|
@@ -223,3 +240,4 @@ export declare class Workspace {
|
|
|
223
240
|
staged: CommandResult;
|
|
224
241
|
}>;
|
|
225
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
|
}
|