@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
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,26 @@ The project follows Semantic Versioning. During `0.x`, minor releases may change
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 1.6.2 - 2026-10-24
|
|
10
|
+
|
|
11
|
+
- Allow one continuity retry per provider in the release gate. Live certification of `v1.6.0` and `v1.6.1` (twice) failed only the multi-process continuity gate, each time on a different provider. A provider whose continuity run fails now gets one fresh retry of all phases and passes only if the retry passes. Every selected provider stays mandatory, and no other gate retries. The continuity evidence records which provider retried, the first attempt's sanitized cause, and the retry outcome. Runtime and CLI behavior and defaults are unchanged (#250).
|
|
12
|
+
- 1.6.1 was tagged but not published, so 1.6.2 is the first npm release carrying the 1.6.1 compaction fix below.
|
|
13
|
+
|
|
14
|
+
## 1.6.1 - 2026-10-24
|
|
15
|
+
|
|
16
|
+
- Mark compacted recollections that precede later user messages. Compaction keeps user steering and recent assistant replies in separate lists, so a stale reply naming a replaced objective could read as newer than the user message that superseded it. In the `v1.6.0` live certification, meta and qwen repeated their previous answer in the continuity gate. Recent entries followed by a later user message now carry a `[before later user messages] ` prefix. The summary schema, strategy id, and defaults are unchanged (#248).
|
|
17
|
+
|
|
18
|
+
## 1.6.0 - 2026-10-24
|
|
19
|
+
|
|
20
|
+
- Prepare minor Harness 1.6.0 for npm `latest`. The minor is opt-in auto-approve. Code stays version 0.4.0. Its source pin moves to Harness 1.6.0 because installed acceptance rejects a candidate that differs from that pin. Published npm Code 0.4.0 remains pinned to Harness 1.5.0 and is not republished. Private Desktop binds to this engine. The representative mapping for `v1.6.0` uses the same Meta, Qwen, and OpenAI models as `v1.5.0`. This preparation creates no tag and dispatches no workflow.
|
|
21
|
+
- Add opt-in auto-approve. Manual stays the default. `auto-edits` can apply file edits inside the workspace without a prompt. `auto-limits` adds commands whose executable is on the allowlist. Network or egress, secrets, `git push`, package publish, writes outside the workspace, and a reached spend or token limit still wait for a person. A mixed batch stays on the review screen. `--approval-mode auto` and `--yes` remain the older non-interactive switch and are not this feature. See [opt-in auto-approve](docs/AUTO_APPROVE.md).
|
|
22
|
+
- Rework the local web workspace. Light-theme controls use theme tokens, the header is one bar, and the first message creates the session. A confirmed execution failure stays editable. The model library, composer, message bubbles, tool pills, and appearance control follow the Harness chat reference.
|
|
23
|
+
- Name each terminal action. Read, search, and edit lines state the target. Consecutive edits of one file collapse to a net diff and an attempt count. The review names the change, counts each file, keeps indentation when a line wraps, and shows NUL, zero-width characters, bidi overrides, tabs, and trailing spaces. The decision is "Approve this change".
|
|
24
|
+
- Keep NUL and other control bytes out of model-facing edit tool schemas. `propose_edits`, `apply_patch`, and the replacement and verified-edit inputs do not advertise `allowControlBytes`. Model-supplied text is still refused, and the host opt-in stays off unless a host call passes it outside the tool input. The stable declaration snapshot records that optional host argument.
|
|
25
|
+
- Pin private Desktop Electron 44.9.3 and its optional `undici` dependency at 7.30.1. Electron 44.5.1 is inside GHSA-jr5w-w6x2-84p9 (`<44.9.1`), and undici 7.29.1 is inside GHSA-4qv8-2p9j-2685, GHSA-f72v-6w45-q472, and GHSA-xjj6-f33q-929c (`<7.30.1`). Desktop behavior is unchanged.
|
|
26
|
+
- Pin the private web Playwright devDependency at 1.64.1 so `playwright install --with-deps chromium` can run on the Ubuntu 26.04 GitHub runner. Browser journeys still launch headless Chromium the same way.
|
|
27
|
+
- Pin the published 2026-10-24 SDK batch: Core 1.36.0, Agents 1.12.0, OpenAI 0.17.0, Anthropic 0.15.0, Qwen 0.18.0, Gemini 0.14.0, Vertex 1.3.0, Azure OpenAI 0.8.0, Bedrock 2.1.0, and Meta 0.3.0. The web client pins React 0.10.0. The root Core override stays one runtime. `zod` is 4.6.7 and `google-auth-library` is 11.3.1 so the new adapter floors resolve to one copy. Harness and CLI defaults are unchanged: shared `topP`, `seed`, and `stopSequences` are not sent, `stopWhen` is not set, and voice is unused. `@types/bun` is 1.4.3. CodeQL init and analyze stay SHA-pinned at `6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee` (v4.39.0).
|
|
28
|
+
|
|
9
29
|
## 1.5.0 - 2026-10-16
|
|
10
30
|
|
|
11
31
|
- Prepare minor Harness 1.5.0 and Code 0.4.0 for npm `latest`. The minor covers the new experimental audit pack, provisional Bedrock and Azure OpenAI routes, and Code web task control; stable root exports and command contracts are unchanged. It ships with the exact Code engine pin, private Desktop binding and an additive representative mapping for `v1.5.0` using the unchanged Meta, Qwen and OpenAI models. Published `latest` is Harness 1.4.1 and Code 0.3.2. Tags and publication follow only through the protected release workflows.
|
package/README.md
CHANGED
|
@@ -159,6 +159,6 @@ Contracts, stability tiers, and the experimental ACP and MCP entry points are in
|
|
|
159
159
|
|
|
160
160
|
## Releases
|
|
161
161
|
|
|
162
|
-
Version `1.
|
|
162
|
+
Version `1.5.0` is the current public npm release. The paired Code package on npm `latest` is `0.4.0`, pinned to that engine. This checkout prepares Version `1.6.2` for `latest`. Code stays version `0.4.0`, and its source pin is Harness `1.6.2` for installed acceptance. Published Code `0.4.0` stays on Harness `1.5.0`. That Harness publication is pending, so the quick start above follows the published `latest` tag. npm `next` is Harness `1.4.0-rc.8` and Code `0.3.0-rc.8`. Confirm dist-tags on the [Code versions](https://www.npmjs.com/package/@zhivex-ai/code?activeTab=versions) and [Harness versions](https://www.npmjs.com/package/@zhivex-ai/harness?activeTab=versions) pages before you install a prerelease.
|
|
163
163
|
|
|
164
164
|
Release channels, publication, and certification records are in the [release procedure](https://github.com/Zhivex/zhivex-harness/blob/main/docs/RELEASE.md). Releases are verified with npm provenance. Source in this checkout can be newer than the public package. See the [changelog](CHANGELOG.md) and the [repository release status](https://raw.githubusercontent.com/Zhivex/zhivex-harness/main/release-status.json).
|
package/SUPPORT.md
CHANGED
|
@@ -13,7 +13,7 @@ The 1.1 support scope is tracked in the [machine-readable support matrix](./docs
|
|
|
13
13
|
- the provider/model combinations marked certified in the package documentation; and
|
|
14
14
|
- HTTPS or explicitly allowed loopback-HTTP MCP endpoints using the documented bounded JSON-RPC subset.
|
|
15
15
|
|
|
16
|
-
Only the most recent published stable minor receives routine fixes. The exact `v1.3.0` tag passed the protected artifact, six-provider live and 42-case representative release jobs. Harness 1.
|
|
16
|
+
Only the most recent published stable minor receives routine fixes. The exact `v1.3.0` tag passed the protected artifact, six-provider live and 42-case representative release jobs. Harness 1.5.0 and Code 0.4.0 are the published stable packages. Code stays version 0.4.0. Its source pin is Harness 1.6.2 for installed acceptance, and published Code 0.4.0 remains pinned to Harness 1.5.0 until 1.6.2 completes verified publication. Gemini, Anthropic and Vertex remain provisional despite the release-bound live gates; the representative repository matrix covers only Meta, Qwen and OpenAI. Live provider evidence is account-, model-, endpoint-, artifact-, and date-dependent; see [docs/LIVE_CERTIFICATION.md](https://github.com/Zhivex/zhivex-harness/blob/main/docs/LIVE_CERTIFICATION.md).
|
|
17
17
|
|
|
18
18
|
## Installation diagnostics
|
|
19
19
|
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
},
|
|
126
126
|
{
|
|
127
127
|
"name": "Workspace",
|
|
128
|
-
"sha256": "sha256:
|
|
128
|
+
"sha256": "sha256:9b8bc9eb0d44bb64e75995483aa9c68f73816359da93299844b8c8290c3a1480"
|
|
129
129
|
},
|
|
130
130
|
{
|
|
131
131
|
"name": "appendUserMessage",
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
},
|
|
166
166
|
{
|
|
167
167
|
"name": "createHarness",
|
|
168
|
-
"sha256": "sha256:
|
|
168
|
+
"sha256": "sha256:2a9567b8785aced05da16f6606cf6e71bad3229d2df0d0b1cf1f5f96e57852f2"
|
|
169
169
|
},
|
|
170
170
|
{
|
|
171
171
|
"name": "createHarnessMcpTools",
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
},
|
|
174
174
|
{
|
|
175
175
|
"name": "createHarnessOciExecutionEnvironment",
|
|
176
|
-
"sha256": "sha256:
|
|
176
|
+
"sha256": "sha256:432ed8e25be522eb6adf03ca61bc01d1f04e3ccafcb9a9d380b6b55130f5a1c5"
|
|
177
177
|
},
|
|
178
178
|
{
|
|
179
179
|
"name": "createHarnessStateBackup",
|
|
@@ -273,7 +273,7 @@
|
|
|
273
273
|
},
|
|
274
274
|
{
|
|
275
275
|
"name": "runHarness",
|
|
276
|
-
"sha256": "sha256:
|
|
276
|
+
"sha256": "sha256:e2da41119fb7a86ae2385a2edacee3221c44ab68bcc9360d5307f7df44bd4982"
|
|
277
277
|
},
|
|
278
278
|
{
|
|
279
279
|
"name": "serializeStreamEvent",
|
|
@@ -427,7 +427,7 @@
|
|
|
427
427
|
},
|
|
428
428
|
{
|
|
429
429
|
"name": "HarnessExecutionSession",
|
|
430
|
-
"sha256": "sha256:
|
|
430
|
+
"sha256": "sha256:6ef99960a68c92cbcf6f1b0d6f65fde844f0b1ee1514b7d1b4df65d752cc4a37"
|
|
431
431
|
},
|
|
432
432
|
{
|
|
433
433
|
"name": "HarnessMcpConfiguration",
|
|
@@ -487,8 +487,8 @@
|
|
|
487
487
|
},
|
|
488
488
|
{
|
|
489
489
|
"name": "ZhivexHarness",
|
|
490
|
-
"sha256": "sha256:
|
|
490
|
+
"sha256": "sha256:959a37960c89176ada62c3d126bb30b49d0621b721b89a5972fcf58a184480c0"
|
|
491
491
|
}
|
|
492
492
|
],
|
|
493
|
-
"digest": "sha256:
|
|
493
|
+
"digest": "sha256:ad2cd323cee44ab26037a02eb9079eecf713771601de3fde44ad2e3b4df85cbc"
|
|
494
494
|
}
|
package/dist/acp-cli.js
CHANGED
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
} from "./index-3ah1f369.js";
|
|
12
12
|
import {
|
|
13
13
|
createHarnessClientAdapter
|
|
14
|
-
} from "./index-
|
|
15
|
-
import"./index-
|
|
14
|
+
} from "./index-paf7z8kn.js";
|
|
15
|
+
import"./index-4mn6m3bq.js";
|
|
16
16
|
import {
|
|
17
17
|
createHarness
|
|
18
|
-
} from "./index-
|
|
19
|
-
import"./index-
|
|
20
|
-
import"./index-
|
|
21
|
-
import"./index-
|
|
18
|
+
} from "./index-pf73y4gt.js";
|
|
19
|
+
import"./index-amjttjaj.js";
|
|
20
|
+
import"./index-2r4f0p4n.js";
|
|
21
|
+
import"./index-w0m1nckc.js";
|
|
22
22
|
import {
|
|
23
23
|
HARNESS_REQUIRED_CAPABILITIES,
|
|
24
24
|
HARNESS_SUBAGENT_PROFILES,
|
|
@@ -35,7 +35,7 @@ import"./index-bt2p8x7r.js";
|
|
|
35
35
|
import {
|
|
36
36
|
usagePricingSchema
|
|
37
37
|
} from "./index-fqqz1yt8.js";
|
|
38
|
-
import"./index-
|
|
38
|
+
import"./index-rz7wmp71.js";
|
|
39
39
|
import {
|
|
40
40
|
FileChangedWhileReadingError,
|
|
41
41
|
FileSizeLimitError,
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/** Opt-in approval policy. Manual is the default. Always-ask reasons are checked before any mode or allowlist. */
|
|
2
|
+
export declare const AUTO_APPROVE_MODES: readonly ["manual", "auto-edits", "auto-limits"];
|
|
3
|
+
export type AutoApproveMode = (typeof AUTO_APPROVE_MODES)[number];
|
|
4
|
+
export interface AutoApprovePolicy {
|
|
5
|
+
mode: AutoApproveMode;
|
|
6
|
+
commandAllowlist: readonly string[];
|
|
7
|
+
rootDir: string;
|
|
8
|
+
overLimit?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface ApprovalPath {
|
|
11
|
+
path: string;
|
|
12
|
+
operation?: "create" | "update" | "delete" | "move" | "restore";
|
|
13
|
+
}
|
|
14
|
+
export interface ApprovalFacts {
|
|
15
|
+
name: string;
|
|
16
|
+
paths: readonly ApprovalPath[];
|
|
17
|
+
commands: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
export type AskReason = "manual" | "not-an-edit" | "not-allowlisted" | "unproven-path" | "network" | "delete-outside" | "write-outside" | "secret" | "git-push" | "package-publish" | "over-limit";
|
|
20
|
+
export type ClassifyResult = {
|
|
21
|
+
decision: "ask";
|
|
22
|
+
reason: AskReason;
|
|
23
|
+
} | {
|
|
24
|
+
decision: "auto";
|
|
25
|
+
reason: "edit-in-root" | "allowlisted-command";
|
|
26
|
+
};
|
|
27
|
+
export interface ApprovalConfigFile {
|
|
28
|
+
mode: AutoApproveMode;
|
|
29
|
+
commandAllowlist: string[];
|
|
30
|
+
}
|
|
31
|
+
export interface AutoApprovalSnapshot {
|
|
32
|
+
path: string;
|
|
33
|
+
before: string | null;
|
|
34
|
+
after: string | null;
|
|
35
|
+
undoable: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface AutoApprovalAuditEntry {
|
|
38
|
+
id: string;
|
|
39
|
+
approvalId: string;
|
|
40
|
+
name: string;
|
|
41
|
+
mode: Exclude<AutoApproveMode, "manual">;
|
|
42
|
+
reason: string;
|
|
43
|
+
timestamp: string;
|
|
44
|
+
snapshots: AutoApprovalSnapshot[];
|
|
45
|
+
}
|
|
46
|
+
export interface PublicAutoApproval {
|
|
47
|
+
id: string;
|
|
48
|
+
name: string;
|
|
49
|
+
mode: Exclude<AutoApproveMode, "manual">;
|
|
50
|
+
reason: string;
|
|
51
|
+
timestamp: string;
|
|
52
|
+
paths: string[];
|
|
53
|
+
undoable: boolean;
|
|
54
|
+
}
|
|
55
|
+
export declare const approvalConfigPath: (rootDir: string) => string;
|
|
56
|
+
export declare const autoApprovalLogPath: (rootDir: string) => string;
|
|
57
|
+
export declare const isAutoApproveMode: (value: string) => value is AutoApproveMode;
|
|
58
|
+
/** Exact executable names only. Wildcards, paths and shell syntax are rejected. */
|
|
59
|
+
export declare const parseAllowlist: (entries: readonly string[]) => string[];
|
|
60
|
+
export declare const parseApprovalConfig: (text: string) => ApprovalConfigFile;
|
|
61
|
+
export declare const resolveAutoApprovePolicy: (input: {
|
|
62
|
+
rootDir: string;
|
|
63
|
+
overLimit?: boolean;
|
|
64
|
+
file?: ApprovalConfigFile | null;
|
|
65
|
+
cliMode?: AutoApproveMode;
|
|
66
|
+
cliAllow?: readonly string[];
|
|
67
|
+
sessionMode?: AutoApproveMode;
|
|
68
|
+
sessionAllow?: readonly string[];
|
|
69
|
+
}) => AutoApprovePolicy;
|
|
70
|
+
export declare const isInsideRoot: (rootDir: string, candidate: string) => boolean;
|
|
71
|
+
export declare const isSecretPath: (candidate: string) => boolean;
|
|
72
|
+
/** Always-ask is decided before mode and before the command allowlist. There is no force flag. */
|
|
73
|
+
export declare const classifyApproval: (facts: ApprovalFacts, policy: AutoApprovePolicy) => ClassifyResult;
|
|
74
|
+
export declare const approvalFacts: (name: string, argumentsText: string) => ApprovalFacts;
|
|
75
|
+
/** A whole pending batch auto-resolves only when every item is auto. One always-ask item keeps the batch for a person. */
|
|
76
|
+
export declare const decideBatch: (items: readonly {
|
|
77
|
+
name?: string;
|
|
78
|
+
arguments?: string;
|
|
79
|
+
}[], policy: AutoApprovePolicy) => {
|
|
80
|
+
auto: true;
|
|
81
|
+
results: {
|
|
82
|
+
decision: "auto";
|
|
83
|
+
reason: "edit-in-root" | "allowlisted-command";
|
|
84
|
+
}[];
|
|
85
|
+
reason?: never;
|
|
86
|
+
} | {
|
|
87
|
+
results?: never;
|
|
88
|
+
auto: false;
|
|
89
|
+
reason: AskReason;
|
|
90
|
+
};
|
|
91
|
+
export declare const snapshotText: (bytes: Buffer | null) => {
|
|
92
|
+
text: null;
|
|
93
|
+
undoable: boolean;
|
|
94
|
+
} | {
|
|
95
|
+
text: string;
|
|
96
|
+
undoable: boolean;
|
|
97
|
+
};
|
|
98
|
+
export declare const buildSnapshots: (argumentsText: string, read: (relativePath: string) => {
|
|
99
|
+
text: string | null;
|
|
100
|
+
undoable: boolean;
|
|
101
|
+
}) => AutoApprovalSnapshot[];
|
|
102
|
+
export declare const planUndo: (entry: AutoApprovalAuditEntry, current: ReadonlyMap<string, string | null>) => {
|
|
103
|
+
ok: false;
|
|
104
|
+
reason: string;
|
|
105
|
+
writes?: never;
|
|
106
|
+
} | {
|
|
107
|
+
reason?: never;
|
|
108
|
+
ok: true;
|
|
109
|
+
writes: {
|
|
110
|
+
path: string;
|
|
111
|
+
contents: string | null;
|
|
112
|
+
}[];
|
|
113
|
+
};
|
|
114
|
+
export declare const publicAutoApproval: (entry: AutoApprovalAuditEntry) => PublicAutoApproval;
|
|
115
|
+
export declare const readAutoApprovals: (rootDir: string) => Promise<AutoApprovalAuditEntry[]>;
|
|
116
|
+
export declare const appendAutoApproval: (rootDir: string, entry: AutoApprovalAuditEntry) => Promise<void>;
|
|
117
|
+
export declare const loadApprovalConfig: (rootDir: string) => Promise<ApprovalConfigFile>;
|
|
118
|
+
export declare const writeApprovalConfig: (rootDir: string, config: ApprovalConfigFile) => Promise<void>;
|
|
119
|
+
export declare const createAutoApprovalEntry: (input: {
|
|
120
|
+
approvalId: string;
|
|
121
|
+
name: string;
|
|
122
|
+
mode: Exclude<AutoApproveMode, "manual">;
|
|
123
|
+
reason: string;
|
|
124
|
+
snapshots: AutoApprovalSnapshot[];
|
|
125
|
+
}) => AutoApprovalAuditEntry;
|
|
126
|
+
export declare const readWorkspaceSnapshot: (rootDir: string, relativePath: string) => Promise<{
|
|
127
|
+
text: string | null;
|
|
128
|
+
undoable: boolean;
|
|
129
|
+
}>;
|
|
130
|
+
export declare const revertAutoApproval: (rootDir: string, id: string) => Promise<{
|
|
131
|
+
ok: false;
|
|
132
|
+
reason: string;
|
|
133
|
+
writes?: never;
|
|
134
|
+
} | {
|
|
135
|
+
ok: true;
|
|
136
|
+
paths: string[];
|
|
137
|
+
}>;
|
package/dist/audit/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
exportHarnessGovernanceReport,
|
|
3
3
|
harnessGovernanceReportSchema
|
|
4
|
-
} from "../index-
|
|
4
|
+
} from "../index-zrz4rs7p.js";
|
|
5
5
|
import"../index-yx52bt4a.js";
|
|
6
6
|
import {
|
|
7
7
|
APPROVAL_HISTORY_KEY,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from "../index-fqqz1yt8.js";
|
|
18
18
|
import {
|
|
19
19
|
HARNESS_VERSION
|
|
20
|
-
} from "../index-
|
|
20
|
+
} from "../index-rz7wmp71.js";
|
|
21
21
|
import {
|
|
22
22
|
readRegularFileNoFollow
|
|
23
23
|
} from "../index-6dxrbnxm.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
exportHarnessGovernanceReport,
|
|
3
3
|
harnessGovernanceReportSchema
|
|
4
|
-
} from "./cli-entry-
|
|
4
|
+
} from "./cli-entry-z1pbjrg4.js";
|
|
5
5
|
import {
|
|
6
6
|
APPROVAL_HISTORY_KEY,
|
|
7
7
|
CliUsageError,
|
|
@@ -17,10 +17,10 @@ import {
|
|
|
17
17
|
openHarnessPersistence,
|
|
18
18
|
readRegularFileNoFollow,
|
|
19
19
|
validateStateDirectory
|
|
20
|
-
} from "./cli-entry-
|
|
20
|
+
} from "./cli-entry-6amx4mcc.js";
|
|
21
21
|
import {
|
|
22
22
|
HARNESS_VERSION
|
|
23
|
-
} from "./cli-entry-
|
|
23
|
+
} from "./cli-entry-10th5awp.js";
|
|
24
24
|
import {
|
|
25
25
|
resolveHarnessConfig
|
|
26
26
|
} from "./cli-entry-hdtfef27.js";
|
|
@@ -61,11 +61,11 @@ import {
|
|
|
61
61
|
withTemporaryHarnessProfiles,
|
|
62
62
|
workspaceCheckpointSchema,
|
|
63
63
|
workspaceRestoreOperationSchema
|
|
64
|
-
} from "./cli-entry-
|
|
64
|
+
} from "./cli-entry-4g2ewcnw.js";
|
|
65
65
|
import {
|
|
66
66
|
exportHarnessGovernanceReport,
|
|
67
67
|
renderHarnessGovernanceMarkdown
|
|
68
|
-
} from "./cli-entry-
|
|
68
|
+
} from "./cli-entry-z1pbjrg4.js";
|
|
69
69
|
import {
|
|
70
70
|
BUDGET_LEDGER_NAMESPACE,
|
|
71
71
|
BUDGET_OWNER_NAMESPACE_KEY,
|
|
@@ -125,14 +125,14 @@ import {
|
|
|
125
125
|
updateCliProfile,
|
|
126
126
|
validateStateDirectory,
|
|
127
127
|
verifyChangeEnvelope
|
|
128
|
-
} from "./cli-entry-
|
|
128
|
+
} from "./cli-entry-6amx4mcc.js";
|
|
129
129
|
import {
|
|
130
130
|
BUN_ENGINE_RANGE,
|
|
131
131
|
HARNESS_VERSION,
|
|
132
132
|
NODE_ENGINE_RANGE,
|
|
133
133
|
formatCliHelp,
|
|
134
134
|
shortCliHelp
|
|
135
|
-
} from "./cli-entry-
|
|
135
|
+
} from "./cli-entry-10th5awp.js";
|
|
136
136
|
import {
|
|
137
137
|
DEFAULT_PROVIDER_REGISTRY,
|
|
138
138
|
HARNESS_SUBAGENT_PROFILES,
|
|
@@ -4091,7 +4091,7 @@ var main = async (argv = process.argv.slice(2)) => {
|
|
|
4091
4091
|
parsedOptions = { ...parsedOptions, prompt: await readStdinTask() };
|
|
4092
4092
|
}
|
|
4093
4093
|
if (parsedOptions.serviceFile) {
|
|
4094
|
-
const { runServiceCli } = await import("./service-client-cli-
|
|
4094
|
+
const { runServiceCli } = await import("./service-client-cli-pp5z4dmf.js");
|
|
4095
4095
|
await runServiceCli(parsedOptions, annotateCliStreamError);
|
|
4096
4096
|
return;
|
|
4097
4097
|
}
|
|
@@ -4156,7 +4156,7 @@ var main = async (argv = process.argv.slice(2)) => {
|
|
|
4156
4156
|
return;
|
|
4157
4157
|
case "export-audit":
|
|
4158
4158
|
case "verify-audit": {
|
|
4159
|
-
const { auditPackCli } = await import("./audit-pack-
|
|
4159
|
+
const { auditPackCli } = await import("./audit-pack-fckpqfsb.js");
|
|
4160
4160
|
await auditPackCli(options);
|
|
4161
4161
|
return;
|
|
4162
4162
|
}
|
|
@@ -444,7 +444,7 @@ Use zhx help all for the full reference and exit codes.`;
|
|
|
444
444
|
// package.json
|
|
445
445
|
var package_default = {
|
|
446
446
|
name: "@zhivex-ai/harness",
|
|
447
|
-
version: "1.
|
|
447
|
+
version: "1.6.2",
|
|
448
448
|
description: "Governed, provider-portable runtime for coding agents with durable approvals and isolated repository execution.",
|
|
449
449
|
type: "module",
|
|
450
450
|
license: "MIT",
|
|
@@ -544,6 +544,7 @@ var package_default = {
|
|
|
544
544
|
"LICENSE",
|
|
545
545
|
"docs/CHANGE_ENVELOPES.md",
|
|
546
546
|
"docs/CLI.md",
|
|
547
|
+
"docs/AUTO_APPROVE.md",
|
|
547
548
|
"docs/WEB_LIMITS.md",
|
|
548
549
|
"docs/CODE_RELEASE.md",
|
|
549
550
|
"docs/CODE_WEB_TASKS.md",
|
|
@@ -660,26 +661,26 @@ var package_default = {
|
|
|
660
661
|
"smoke:mcp:installed": "bun run build && bun scripts/mcp-installed-smoke.ts"
|
|
661
662
|
},
|
|
662
663
|
dependencies: {
|
|
663
|
-
"@zhivex-ai/agents": "1.
|
|
664
|
-
"@zhivex-ai/anthropic": "0.
|
|
665
|
-
"@zhivex-ai/azure-openai": "0.
|
|
666
|
-
"@zhivex-ai/bedrock": "2.0
|
|
667
|
-
"@zhivex-ai/core": "1.
|
|
668
|
-
"@zhivex-ai/gemini": "0.
|
|
669
|
-
"@zhivex-ai/meta": "0.
|
|
670
|
-
"@zhivex-ai/openai": "0.
|
|
671
|
-
"@zhivex-ai/qwen": "0.
|
|
672
|
-
"@zhivex-ai/vertex": "1.
|
|
673
|
-
"google-auth-library": "11.1
|
|
674
|
-
zod: "4.6.
|
|
664
|
+
"@zhivex-ai/agents": "1.12.0",
|
|
665
|
+
"@zhivex-ai/anthropic": "0.15.0",
|
|
666
|
+
"@zhivex-ai/azure-openai": "0.8.0",
|
|
667
|
+
"@zhivex-ai/bedrock": "2.1.0",
|
|
668
|
+
"@zhivex-ai/core": "1.36.0",
|
|
669
|
+
"@zhivex-ai/gemini": "0.14.0",
|
|
670
|
+
"@zhivex-ai/meta": "0.3.0",
|
|
671
|
+
"@zhivex-ai/openai": "0.17.0",
|
|
672
|
+
"@zhivex-ai/qwen": "0.18.0",
|
|
673
|
+
"@zhivex-ai/vertex": "1.3.0",
|
|
674
|
+
"google-auth-library": "11.3.1",
|
|
675
|
+
zod: "4.6.7"
|
|
675
676
|
},
|
|
676
677
|
overrides: {
|
|
677
|
-
"@zhivex-ai/core": "1.
|
|
678
|
+
"@zhivex-ai/core": "1.36.0"
|
|
678
679
|
},
|
|
679
680
|
devDependencies: {
|
|
680
681
|
"@agentclientprotocol/sdk": "1.8.0",
|
|
681
682
|
"@modelcontextprotocol/server": "2.3.1",
|
|
682
|
-
"@types/bun": "1.4.
|
|
683
|
+
"@types/bun": "1.4.3",
|
|
683
684
|
"@types/node": "^26.6.5",
|
|
684
685
|
typescript: "7.0.2",
|
|
685
686
|
"typescript-compiler-api": "npm:typescript@6.0.3"
|
|
@@ -97,10 +97,10 @@ import {
|
|
|
97
97
|
withWorkspaceMutation,
|
|
98
98
|
workBudgetReached,
|
|
99
99
|
workspaceFilePathSchema
|
|
100
|
-
} from "./cli-entry-
|
|
100
|
+
} from "./cli-entry-6amx4mcc.js";
|
|
101
101
|
import {
|
|
102
102
|
HARNESS_VERSION
|
|
103
|
-
} from "./cli-entry-
|
|
103
|
+
} from "./cli-entry-10th5awp.js";
|
|
104
104
|
import {
|
|
105
105
|
DEFAULT_PROVIDER_REGISTRY,
|
|
106
106
|
HARNESS_CONFIG_SCHEMA_VERSION,
|
|
@@ -4979,6 +4979,7 @@ var record = (value) => value !== null && typeof value === "object" && !Array.is
|
|
|
4979
4979
|
var LOCAL_TOOLS = LOCAL_TOOL_NAMES;
|
|
4980
4980
|
var MAX_USER_STEERING = 10;
|
|
4981
4981
|
var CONTEXT_PRIORITY = "Latest user > chronological user steering > historical objective. Assistant never overrides user facts. Questions retain task.";
|
|
4982
|
+
var EARLIER = "[before later user messages] ";
|
|
4982
4983
|
var summarizeHarnessMessages = (messages, maxCharacters = 4000) => {
|
|
4983
4984
|
const redaction = createRedactionPolicy3({ includeEmails: true });
|
|
4984
4985
|
const clean = (text, limit = 512) => redaction.redactText(text).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);
|
|
@@ -5004,6 +5005,12 @@ var summarizeHarnessMessages = (messages, maxCharacters = 4000) => {
|
|
|
5004
5005
|
omitted = true;
|
|
5005
5006
|
}
|
|
5006
5007
|
};
|
|
5008
|
+
const markRecentEarlier = () => {
|
|
5009
|
+
const marked = recent.map((value) => value.startsWith(EARLIER) ? value : `${EARLIER}${value}`);
|
|
5010
|
+
recent.length = 0;
|
|
5011
|
+
for (const value of marked)
|
|
5012
|
+
add(recent, value, 4);
|
|
5013
|
+
};
|
|
5007
5014
|
const rememberObservation = (tool2, detail) => {
|
|
5008
5015
|
if (typeof tool2 !== "string" || !LOCAL_TOOLS.has(tool2) || typeof detail !== "string")
|
|
5009
5016
|
return;
|
|
@@ -5121,6 +5128,7 @@ var summarizeHarnessMessages = (messages, maxCharacters = 4000) => {
|
|
|
5121
5128
|
objective = text;
|
|
5122
5129
|
if (historicalObjective)
|
|
5123
5130
|
add(steering, text, MAX_USER_STEERING);
|
|
5131
|
+
markRecentEarlier();
|
|
5124
5132
|
} else
|
|
5125
5133
|
add(recent, `${message.role}: ${text}`, 4);
|
|
5126
5134
|
} else if (part.type === "tool-call") {
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
CLI_OPTION_DEFINITIONS,
|
|
3
3
|
resolveHelpTopic,
|
|
4
4
|
validateCliCommandOptions
|
|
5
|
-
} from "./cli-entry-
|
|
5
|
+
} from "./cli-entry-10th5awp.js";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_PROVIDER_REGISTRY,
|
|
8
8
|
HARNESS_EXECUTION_POLICY_VERSION,
|
|
@@ -3994,6 +3994,55 @@ var runPortableProcess = async (command, options = {}) => {
|
|
|
3994
3994
|
}
|
|
3995
3995
|
};
|
|
3996
3996
|
|
|
3997
|
+
// src/workspace/text-integrity.ts
|
|
3998
|
+
var controlLabel = (code) => code === 0 ? "NUL byte" : `U+${code.toString(16).padStart(4, "0")}`;
|
|
3999
|
+
var firstDisallowedControl = (text) => {
|
|
4000
|
+
let line = 1;
|
|
4001
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
4002
|
+
const code = text.charCodeAt(index);
|
|
4003
|
+
if (code === 10) {
|
|
4004
|
+
line += 1;
|
|
4005
|
+
continue;
|
|
4006
|
+
}
|
|
4007
|
+
if (code === 9 || code === 13)
|
|
4008
|
+
continue;
|
|
4009
|
+
if (code < 32 || code === 127 || code >= 128 && code <= 159)
|
|
4010
|
+
return { line, label: controlLabel(code) };
|
|
4011
|
+
}
|
|
4012
|
+
return;
|
|
4013
|
+
};
|
|
4014
|
+
var firstDisallowedControlBytes = (bytes) => {
|
|
4015
|
+
let line = 1;
|
|
4016
|
+
for (let index = 0;index < bytes.length; index += 1) {
|
|
4017
|
+
const code = bytes[index] ?? 0;
|
|
4018
|
+
if (code === 10) {
|
|
4019
|
+
line += 1;
|
|
4020
|
+
continue;
|
|
4021
|
+
}
|
|
4022
|
+
if (code === 9 || code === 13)
|
|
4023
|
+
continue;
|
|
4024
|
+
if (code < 32 || code === 127)
|
|
4025
|
+
return { line, label: controlLabel(code) };
|
|
4026
|
+
}
|
|
4027
|
+
return;
|
|
4028
|
+
};
|
|
4029
|
+
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.`;
|
|
4030
|
+
var binaryAfterEditMessage = (hit) => `file is binary after edit: ${hit.label} at L${hit.line}. The edit was reverted. Read the file again before retrying.`;
|
|
4031
|
+
var textGuards = {
|
|
4032
|
+
beforeWrite(content, allowControlBytes) {
|
|
4033
|
+
if (allowControlBytes)
|
|
4034
|
+
return;
|
|
4035
|
+
const problem = firstDisallowedControl(content);
|
|
4036
|
+
if (problem)
|
|
4037
|
+
throw new HarnessWorkspaceError(refuseControlMessage(problem));
|
|
4038
|
+
},
|
|
4039
|
+
afterWrite(bytes, allowControlBytes) {
|
|
4040
|
+
if (allowControlBytes)
|
|
4041
|
+
return;
|
|
4042
|
+
return firstDisallowedControlBytes(bytes);
|
|
4043
|
+
}
|
|
4044
|
+
};
|
|
4045
|
+
|
|
3997
4046
|
// src/workspace/path-policy.ts
|
|
3998
4047
|
var HARD_IGNORES = new Set([".git", ".next", ".turbo", ".zhivex-harness", "coverage", "dist", "node_modules"]);
|
|
3999
4048
|
var isSensitiveName = (name) => {
|
|
@@ -4323,8 +4372,11 @@ class Workspace {
|
|
|
4323
4372
|
maxBytes: MAX_FILE_BYTES
|
|
4324
4373
|
});
|
|
4325
4374
|
const { contents } = file;
|
|
4326
|
-
if (!allowBinary && contents.includes(0))
|
|
4327
|
-
|
|
4375
|
+
if (!allowBinary && contents.includes(0)) {
|
|
4376
|
+
const hit = firstDisallowedControlBytes(contents);
|
|
4377
|
+
const where = hit ? `: ${hit.label} at L${hit.line}` : "";
|
|
4378
|
+
throw new HarnessWorkspaceError(`The file appears to be binary and cannot be read as text${where}.`);
|
|
4379
|
+
}
|
|
4328
4380
|
return {
|
|
4329
4381
|
path: wirePath(path11.relative(this.root, safe.path)),
|
|
4330
4382
|
absolutePath: safe.path,
|
|
@@ -4991,19 +5043,23 @@ class Workspace {
|
|
|
4991
5043
|
if (position < 0 || text.indexOf(edit.oldText, position + 1) >= 0) {
|
|
4992
5044
|
throw new HarnessWorkspaceError("Replacement requires exactly one literal match; include more surrounding context.");
|
|
4993
5045
|
}
|
|
4994
|
-
return [{
|
|
5046
|
+
return [{
|
|
5047
|
+
path: edit.path,
|
|
5048
|
+
expectedDigest: edit.expectedDigest,
|
|
5049
|
+
content: text.slice(0, position) + edit.newText + text.slice(position + edit.oldText.length)
|
|
5050
|
+
}];
|
|
4995
5051
|
}
|
|
4996
|
-
async applyReplacement(input) {
|
|
5052
|
+
async applyReplacement(input, host = {}) {
|
|
4997
5053
|
const changes = await this.replacementChanges(input);
|
|
4998
|
-
return this.applyPatch({ proposalId: createEditProposal({ changes }).proposalId, changes });
|
|
5054
|
+
return this.applyPatch({ proposalId: createEditProposal({ changes }).proposalId, changes }, host);
|
|
4999
5055
|
}
|
|
5000
|
-
async applyPatch(input) {
|
|
5001
|
-
return this.applyPatchWithModes(input, new Map);
|
|
5056
|
+
async applyPatch(input, host = {}) {
|
|
5057
|
+
return this.applyPatchWithModes(input, new Map, undefined, host);
|
|
5002
5058
|
}
|
|
5003
|
-
async applyPatchWithModes(input, modes, assertActive) {
|
|
5004
|
-
return withWorkspaceMutation(this.root, () => this.applyPatchWithModesLocked(input, modes, assertActive));
|
|
5059
|
+
async applyPatchWithModes(input, modes, assertActive, host = {}) {
|
|
5060
|
+
return withWorkspaceMutation(this.root, () => this.applyPatchWithModesLocked(input, modes, assertActive, host));
|
|
5005
5061
|
}
|
|
5006
|
-
async applyPatchWithModesLocked(input, modes, assertActive) {
|
|
5062
|
+
async applyPatchWithModesLocked(input, modes, assertActive, host = {}) {
|
|
5007
5063
|
const proposal = validateEditProposal(input);
|
|
5008
5064
|
this.checkAuditCapacity(proposal.changes.length);
|
|
5009
5065
|
const targetPaths = new Set(proposal.changes.map((change) => change.path));
|
|
@@ -5048,6 +5104,7 @@ class Workspace {
|
|
|
5048
5104
|
throw new HarnessWorkspaceError(`Stale patch mode rejected for ${change.path}: expected ${modeBinding.beforeMode.toString(8)}, found ${before.mode.toString(8)}.`);
|
|
5049
5105
|
}
|
|
5050
5106
|
}
|
|
5107
|
+
textGuards.beforeWrite(change.content, host.allowControlBytes === true);
|
|
5051
5108
|
const temporary = await this.stageFile(safe.path, change.content, modeBinding?.afterMode ?? before?.mode ?? 420);
|
|
5052
5109
|
prepared.push({
|
|
5053
5110
|
change,
|
|
@@ -5087,6 +5144,13 @@ class Workspace {
|
|
|
5087
5144
|
await unlink2(item.temporary);
|
|
5088
5145
|
}
|
|
5089
5146
|
committed.push(item);
|
|
5147
|
+
const published = await readRegularFileNoFollow(item.target, {
|
|
5148
|
+
label: "The published file",
|
|
5149
|
+
maxBytes: MAX_FILE_BYTES
|
|
5150
|
+
});
|
|
5151
|
+
const corrupt = textGuards.afterWrite(published.contents, host.allowControlBytes === true);
|
|
5152
|
+
if (corrupt)
|
|
5153
|
+
throw new HarnessWorkspaceError(binaryAfterEditMessage(corrupt));
|
|
5090
5154
|
await assertActive?.();
|
|
5091
5155
|
}
|
|
5092
5156
|
} catch (error) {
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
readTaskAcceptanceLedger,
|
|
11
11
|
taskCheckReceiptSchema,
|
|
12
12
|
verifyChangeEnvelope
|
|
13
|
-
} from "./cli-entry-
|
|
13
|
+
} from "./cli-entry-6amx4mcc.js";
|
|
14
14
|
import {
|
|
15
15
|
HARNESS_VERSION
|
|
16
|
-
} from "./cli-entry-
|
|
16
|
+
} from "./cli-entry-10th5awp.js";
|
|
17
17
|
|
|
18
18
|
// src/persistence/governance-report.ts
|
|
19
19
|
import { createHash as createHash2 } from "node:crypto";
|
package/dist/cli.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
|
}
|