@zhivex-ai/harness 1.5.0 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +3 -1
  3. package/SUPPORT.md +1 -1
  4. package/contracts/stable-api-signatures.json +7 -7
  5. package/dist/acp-cli.js +7 -7
  6. package/dist/approvals/auto-approve.d.ts +137 -0
  7. package/dist/audit/index.js +2 -2
  8. package/dist/{audit-pack-kvw7ng34.js → audit-pack-s9n693by.js} +3 -3
  9. package/dist/{cli-entry-75j3r49b.js → cli-entry-1epvek01.js} +2 -2
  10. package/dist/{cli-entry-q8bgkn9m.js → cli-entry-mbcq96nx.js} +18 -17
  11. package/dist/{cli-entry-928p8h41.js → cli-entry-nyzyvf24.js} +75 -11
  12. package/dist/{cli-entry-h2yc9x37.js → cli-entry-rsx0wpvs.js} +10 -2
  13. package/dist/{cli-vyrytn3m.js → cli-yxvq8qf1.js} +6 -6
  14. package/dist/cli.js +2 -2
  15. package/dist/compat/code-support.js +5 -5
  16. package/dist/engine/acp.js +8 -8
  17. package/dist/engine/client.js +7 -7
  18. package/dist/engine/desktop-state-v1.js +4 -4
  19. package/dist/engine/index.js +10 -10
  20. package/dist/engine/mcp-stdio-v1.js +4 -4
  21. package/dist/engine/service.js +8 -8
  22. package/dist/hostile-repository-demo.js +100 -27
  23. package/dist/{index-vvzc5ezz.js → index-2yyfg69h.js} +1 -1
  24. package/dist/{index-pq3kz1r8.js → index-3wwfn5n6.js} +18 -17
  25. package/dist/{index-63gbp0cz.js → index-4mn6m3bq.js} +1 -1
  26. package/dist/{index-jpdvn09j.js → index-5nmm0642.js} +1 -1
  27. package/dist/{index-wwthv10a.js → index-amjttjaj.js} +1 -1
  28. package/dist/{index-5zbq9ztj.js → index-hhrnrqfc.js} +1 -1
  29. package/dist/{index-vc7yxz50.js → index-j466sekf.js} +4 -4
  30. package/dist/{index-96ezrvg2.js → index-nxmk6re7.js} +2 -2
  31. package/dist/{index-xfedewpt.js → index-rgqzdgv6.js} +2 -2
  32. package/dist/{index-6fp7kdpx.js → index-vn91b9z1.js} +12 -4
  33. package/dist/{index-ggstwr0v.js → index-w0m1nckc.js} +74 -10
  34. package/dist/index.js +9 -9
  35. package/dist/providers/bedrock.d.ts +1 -1
  36. package/dist/review/reveal-invisible.d.ts +13 -0
  37. package/dist/service-cli.js +8 -8
  38. package/dist/{service-client-cli-v6qptg9x.js → service-client-cli-xbj7j5kj.js} +3 -3
  39. package/dist/verify-historical-migrations.js +100 -27
  40. package/dist/workspace/text-integrity.d.ts +19 -0
  41. package/dist/workspace/workspace.d.ts +8 -3
  42. package/dist/zhx.js +2 -2
  43. package/docs/AUTO_APPROVE.md +51 -0
  44. package/docs/AZURE_OPENAI.md +4 -4
  45. package/docs/BEDROCK.md +4 -4
  46. package/docs/CLI.md +13 -0
  47. package/docs/CODE_RELEASE.md +21 -0
  48. package/docs/CREDENTIALS.md +1 -1
  49. package/docs/DURABLE_OPERATIONS.md +1 -1
  50. package/docs/ENGINE_API.md +1 -1
  51. package/docs/EXTENSIBILITY.md +1 -1
  52. package/docs/FIRST_USE.md +6 -6
  53. package/docs/MINIMUM_TASK_AUTHORITY.md +5 -5
  54. package/docs/PROJECT_MEMORY.md +1 -1
  55. package/docs/TOOL_POLICY.md +3 -0
  56. package/docs/VERTEX.md +2 -2
  57. package/docs/WEB_LIMITS.md +2 -2
  58. package/docs/adr/0002-isolated-mcp-stdio.md +1 -1
  59. package/evaluations/representative-assembly-matrix.json +37 -1
  60. package/examples/README.md +3 -3
  61. package/package.json +18 -17
package/CHANGELOG.md CHANGED
@@ -6,6 +6,33 @@ The project follows Semantic Versioning. During `0.x`, minor releases may change
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 1.6.3 - 2026-11-10
10
+
11
+ - Prepare a maintenance patch for npm `latest`, paired with Code 0.4.2 and the private Desktop engine binding. Existing public APIs, storage formats, model defaults and provider tiers are unchanged. Publication and release-bound live certification are pending.
12
+ - Include the keyring 2.1.2 update and dependency security maintenance for the private Web/Desktop clients.
13
+ - Update the development dependency `@agentclientprotocol/sdk` from 1.8.0 to 2.1.0. SDK 2.1 removes `ClientSideConnection`; the Docker interoperability client now uses `client().connect(...)` and the same ACP v1 methods. `@modelcontextprotocol/server` stays at 2.3.1 because that peer is optional and this client does not import it. The Harness ACP server is unchanged.
14
+ - Pin `@zhivex-ai/core` 1.37.0, including the root override, the Code and web package pins, and a web override so the published Harness dependency uses that same runtime, and `@zhivex-ai/qwen` 0.18.1. Qwen 0.18.1 depends on Core `^1.37.0`. Harness and CLI defaults are unchanged. The Qwen adapter rejects non-string portable-decision instructions and adds PixVerse V6 request shaping; Harness does not expose that video surface.
15
+
16
+ ## 1.6.2 - 2026-10-24
17
+
18
+ - 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).
19
+ - 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.
20
+
21
+ ## 1.6.1 - 2026-10-24
22
+
23
+ - 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).
24
+
25
+ ## 1.6.0 - 2026-10-24
26
+
27
+ - 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.
28
+ - 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).
29
+ - 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.
30
+ - 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".
31
+ - 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.
32
+ - 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.
33
+ - 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.
34
+ - 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).
35
+
9
36
  ## 1.5.0 - 2026-10-16
10
37
 
11
38
  - 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,8 @@ Contracts, stability tiers, and the experimental ACP and MCP entry points are in
159
159
 
160
160
  ## Releases
161
161
 
162
- Version `1.4.1` is the current public npm release. The paired Code package on npm `latest` is `0.3.2`. This checkout prepares Version `1.5.0` and Code `0.4.0` for `latest`. That 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.
162
+ Version `1.6.3` is the prepared source candidate, paired with Code `0.4.2`; publication and release-bound live certification are pending.
163
+
164
+ Version `1.6.2` is the current public npm release. The Code package on npm `latest` is `0.4.1`, pinned exactly to Harness `1.6.2`. 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
165
 
164
166
  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.3.x and Code 0.2.x are the supported stable lines until 1.5.0 and Code 0.4.0 complete 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).
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 0.4.1, pinned exactly to Harness 1.6.2, is published on `latest`. 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:42b53815d22e2734371ae5360f059779516f7304bc9a1941dc208d12b67f6a60"
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:0bf4f22db95c409dfa85fce488984f06efa84491fc7d4ef2351a053ea32701f3"
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:6ae2d5238e49299c4c0ee9d9b5f0ddae4de86db9387c8469f7fd6f4518e20e6e"
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:82a768885893267b4c24aae1923726ae59c048f73c34ce1e92d0355981c6d46a"
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:15843c59747ef8d072a66f19c88031a2a2981be3cdebf3aabb836132b6f7c4b9"
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:2911bde71fdae2dff26f766d068c560375757fb6575f5aaec02ff3346c46ab94"
490
+ "sha256": "sha256:959a37960c89176ada62c3d126bb30b49d0621b721b89a5972fcf58a184480c0"
491
491
  }
492
492
  ],
493
- "digest": "sha256:50be2a81cd0e7601d5610008081c406cecf9855c5ffd42d2faffba521b08873e"
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-vc7yxz50.js";
15
- import"./index-63gbp0cz.js";
14
+ } from "./index-j466sekf.js";
15
+ import"./index-4mn6m3bq.js";
16
16
  import {
17
17
  createHarness
18
- } from "./index-6fp7kdpx.js";
19
- import"./index-wwthv10a.js";
20
- import"./index-5zbq9ztj.js";
21
- import"./index-ggstwr0v.js";
18
+ } from "./index-vn91b9z1.js";
19
+ import"./index-amjttjaj.js";
20
+ import"./index-hhrnrqfc.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-pq3kz1r8.js";
38
+ import"./index-3wwfn5n6.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
+ }>;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  exportHarnessGovernanceReport,
3
3
  harnessGovernanceReportSchema
4
- } from "../index-jpdvn09j.js";
4
+ } from "../index-5nmm0642.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-pq3kz1r8.js";
20
+ } from "../index-3wwfn5n6.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-75j3r49b.js";
4
+ } from "./cli-entry-1epvek01.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-928p8h41.js";
20
+ } from "./cli-entry-nyzyvf24.js";
21
21
  import {
22
22
  HARNESS_VERSION
23
- } from "./cli-entry-q8bgkn9m.js";
23
+ } from "./cli-entry-mbcq96nx.js";
24
24
  import {
25
25
  resolveHarnessConfig
26
26
  } from "./cli-entry-hdtfef27.js";
@@ -10,10 +10,10 @@ import {
10
10
  readTaskAcceptanceLedger,
11
11
  taskCheckReceiptSchema,
12
12
  verifyChangeEnvelope
13
- } from "./cli-entry-928p8h41.js";
13
+ } from "./cli-entry-nyzyvf24.js";
14
14
  import {
15
15
  HARNESS_VERSION
16
- } from "./cli-entry-q8bgkn9m.js";
16
+ } from "./cli-entry-mbcq96nx.js";
17
17
 
18
18
  // src/persistence/governance-report.ts
19
19
  import { createHash as createHash2 } from "node:crypto";
@@ -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.5.0",
447
+ version: "1.6.3",
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.11.0",
664
- "@zhivex-ai/anthropic": "0.14.0",
665
- "@zhivex-ai/azure-openai": "0.7.8",
666
- "@zhivex-ai/bedrock": "2.0.7",
667
- "@zhivex-ai/core": "1.34.0",
668
- "@zhivex-ai/gemini": "0.13.0",
669
- "@zhivex-ai/meta": "0.2.9",
670
- "@zhivex-ai/openai": "0.16.0",
671
- "@zhivex-ai/qwen": "0.16.4",
672
- "@zhivex-ai/vertex": "1.2.7",
673
- "google-auth-library": "11.1.0",
674
- zod: "4.6.5"
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.37.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.1",
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.34.0"
678
+ "@zhivex-ai/core": "1.37.0"
678
679
  },
679
680
  devDependencies: {
680
- "@agentclientprotocol/sdk": "1.8.0",
681
+ "@agentclientprotocol/sdk": "2.1.0",
681
682
  "@modelcontextprotocol/server": "2.3.1",
682
- "@types/bun": "1.4.2",
683
+ "@types/bun": "1.4.4",
683
684
  "@types/node": "^26.6.5",
684
685
  typescript: "7.0.2",
685
686
  "typescript-compiler-api": "npm:typescript@6.0.3"
@@ -690,7 +691,7 @@ var package_default = {
690
691
  },
691
692
  packageManager: "bun@1.4.0",
692
693
  optionalDependencies: {
693
- "@napi-rs/keyring": "2.1.1"
694
+ "@napi-rs/keyring": "2.1.2"
694
695
  }
695
696
  };
696
697
 
@@ -2,7 +2,7 @@ import {
2
2
  CLI_OPTION_DEFINITIONS,
3
3
  resolveHelpTopic,
4
4
  validateCliCommandOptions
5
- } from "./cli-entry-q8bgkn9m.js";
5
+ } from "./cli-entry-mbcq96nx.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
- throw new HarnessWorkspaceError("The file appears to be binary and cannot be read as text.");
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 [{ path: edit.path, expectedDigest: edit.expectedDigest, content: text.slice(0, position) + edit.newText + text.slice(position + edit.oldText.length) }];
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) {
@@ -97,10 +97,10 @@ import {
97
97
  withWorkspaceMutation,
98
98
  workBudgetReached,
99
99
  workspaceFilePathSchema
100
- } from "./cli-entry-928p8h41.js";
100
+ } from "./cli-entry-nyzyvf24.js";
101
101
  import {
102
102
  HARNESS_VERSION
103
- } from "./cli-entry-q8bgkn9m.js";
103
+ } from "./cli-entry-mbcq96nx.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") {
@@ -61,11 +61,11 @@ import {
61
61
  withTemporaryHarnessProfiles,
62
62
  workspaceCheckpointSchema,
63
63
  workspaceRestoreOperationSchema
64
- } from "./cli-entry-h2yc9x37.js";
64
+ } from "./cli-entry-rsx0wpvs.js";
65
65
  import {
66
66
  exportHarnessGovernanceReport,
67
67
  renderHarnessGovernanceMarkdown
68
- } from "./cli-entry-75j3r49b.js";
68
+ } from "./cli-entry-1epvek01.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-928p8h41.js";
128
+ } from "./cli-entry-nyzyvf24.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-q8bgkn9m.js";
135
+ } from "./cli-entry-mbcq96nx.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-v6qptg9x.js");
4094
+ const { runServiceCli } = await import("./service-client-cli-xbj7j5kj.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-kvw7ng34.js");
4159
+ const { auditPackCli } = await import("./audit-pack-s9n693by.js");
4160
4160
  await auditPackCli(options);
4161
4161
  return;
4162
4162
  }
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-q8bgkn9m.js";
6
+ } from "./cli-entry-mbcq96nx.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-vyrytn3m.js");
44
+ const { runCli } = await import("./cli-yxvq8qf1.js");
45
45
  await runCli(argv);
46
46
  }