@zhivex-ai/harness 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { type JsonValue, type McpClient, type McpListedTool, type ToolSet } from "@zhivex-ai/core";
2
+ export declare const HARNESS_MCP_CONFIG_SCHEMA_VERSION: 1;
3
+ export declare const HARNESS_MCP_PERMISSIONS: readonly ["read", "network", "write", "external-side-effect"];
4
+ export type HarnessMcpPermission = (typeof HARNESS_MCP_PERMISSIONS)[number];
5
+ export type HarnessMcpTransport = "http" | "custom";
6
+ export interface HarnessMcpServerConfig {
7
+ name: string;
8
+ transport: HarnessMcpTransport;
9
+ url?: string;
10
+ includeTools: readonly string[];
11
+ excludeTools: readonly string[];
12
+ toolNamePrefix: string;
13
+ permissions: readonly HarnessMcpPermission[];
14
+ headerEnv: Readonly<Record<string, string>>;
15
+ trustServerToolAnnotations: boolean;
16
+ maxListPages: number;
17
+ maxListedTools: number;
18
+ listToolsTimeoutMs: number;
19
+ callToolTimeoutMs: number;
20
+ maxOutputBytes: number;
21
+ }
22
+ export interface HarnessMcpConfiguration {
23
+ schemaVersion: typeof HARNESS_MCP_CONFIG_SCHEMA_VERSION;
24
+ servers: readonly HarnessMcpServerConfig[];
25
+ }
26
+ export type HarnessMcpClients = Readonly<Record<string, McpClient>>;
27
+ export declare const normalizeHarnessMcpConfiguration: (value: unknown) => HarnessMcpConfiguration;
28
+ export declare const loadHarnessMcpConfiguration: (workspace: string, configPath: string) => Promise<HarnessMcpConfiguration>;
29
+ type FetchImplementation = typeof fetch;
30
+ export declare const createHttpMcpClient: (server: HarnessMcpServerConfig, env?: NodeJS.ProcessEnv, fetchImplementation?: FetchImplementation) => McpClient;
31
+ export declare const createHarnessMcpTools: (configuration: HarnessMcpConfiguration, options?: {
32
+ clients?: HarnessMcpClients;
33
+ env?: NodeJS.ProcessEnv;
34
+ fetchImplementation?: FetchImplementation;
35
+ }) => Promise<ToolSet>;
36
+ export declare const mcpConfigurationFingerprintInput: (configuration: HarnessMcpConfiguration) => JsonValue;
37
+ export type { McpClient, McpListedTool };
@@ -0,0 +1,234 @@
1
+ import { type AgentRunState, type AgentStatus } from "@zhivex-ai/agents";
2
+ import type { AgentMemoryStore, AgentRunStore } from "@zhivex-ai/agents/ops";
3
+ import { type HarnessConfig, type HarnessStoreBackend } from "./config.js";
4
+ export declare const HARNESS_SQLITE_FILE = "operations.sqlite";
5
+ export declare const HARNESS_OPERATIONS_SCHEMA_VERSION: 1;
6
+ export declare const TERMINAL_RUN_STATUSES: readonly ["completed", "failed", "cancelled", "timed_out"];
7
+ export interface HarnessMigrationResult {
8
+ scannedRuns: number;
9
+ migratedRuns: number;
10
+ migratedToolCalls: number;
11
+ }
12
+ export interface HarnessPersistence {
13
+ backend: HarnessStoreBackend;
14
+ store: AgentRunStore;
15
+ memory: AgentMemoryStore;
16
+ databasePath?: string;
17
+ migration: HarnessMigrationResult;
18
+ close(): void;
19
+ }
20
+ export interface HarnessRunQuery {
21
+ statuses?: AgentStatus[];
22
+ limit?: number;
23
+ cursor?: string;
24
+ updatedAfter?: number;
25
+ updatedBefore?: number;
26
+ }
27
+ export declare const listHarnessRuns: (store: AgentRunStore, config: HarnessConfig, query?: HarnessRunQuery) => Promise<{
28
+ nextCursor?: string;
29
+ schemaVersion: 1;
30
+ kind: "run-list";
31
+ scope: import("@zhivex-ai/core").AgentStoreScope;
32
+ backend: "sqlite" | "file";
33
+ runs: {
34
+ runId: string;
35
+ revision: number;
36
+ status: AgentStatus;
37
+ provider: string;
38
+ model: string;
39
+ agentId: string | undefined;
40
+ parentRunId: string | undefined;
41
+ idempotencyKey: string | undefined;
42
+ scope: import("@zhivex-ai/core").AgentStoreScope | undefined;
43
+ steps: number;
44
+ toolCalls: number;
45
+ toolErrors: number;
46
+ pendingApprovals: number;
47
+ compactions: number;
48
+ childRuns: number;
49
+ usage: import("@zhivex-ai/core").TokenUsage | undefined;
50
+ startedAt: number | undefined;
51
+ updatedAt: number | undefined;
52
+ cancelledAt: number | undefined;
53
+ cancellationReason: string | undefined;
54
+ }[];
55
+ }>;
56
+ export declare const inspectHarnessRun: (store: AgentRunStore, config: HarnessConfig, runId: string) => Promise<{
57
+ schemaVersion: 1;
58
+ kind: "run-inspection";
59
+ run: {
60
+ runId: string;
61
+ revision: number;
62
+ status: AgentStatus;
63
+ provider: string;
64
+ model: string;
65
+ agentId: string | undefined;
66
+ parentRunId: string | undefined;
67
+ idempotencyKey: string | undefined;
68
+ scope: import("@zhivex-ai/core").AgentStoreScope | undefined;
69
+ steps: number;
70
+ toolCalls: number;
71
+ toolErrors: number;
72
+ pendingApprovals: number;
73
+ compactions: number;
74
+ childRuns: number;
75
+ usage: import("@zhivex-ai/core").TokenUsage | undefined;
76
+ startedAt: number | undefined;
77
+ updatedAt: number | undefined;
78
+ cancelledAt: number | undefined;
79
+ cancellationReason: string | undefined;
80
+ };
81
+ budget: import("@zhivex-ai/core").AgentBudgetStatus;
82
+ snapshot: {
83
+ runId: string;
84
+ agentId?: string;
85
+ status: AgentStatus;
86
+ provider: string;
87
+ modelId: string;
88
+ steps: number;
89
+ toolCalls: import("@zhivex-ai/core").ToolCall[];
90
+ childRuns: NonNullable<AgentRunState["childRuns"]>;
91
+ compactions: NonNullable<AgentRunState["compactions"]>;
92
+ usage?: AgentRunState["usage"];
93
+ error?: AgentRunState["error"];
94
+ pendingApprovals: AgentRunState["pendingApprovals"];
95
+ };
96
+ trace: import("@zhivex-ai/core").AgentTraceArtifact;
97
+ hierarchy: import("@zhivex-ai/core").HierarchicalAgentTrace | undefined;
98
+ ledger: {
99
+ snapshot: {
100
+ runId: string;
101
+ agentId?: string;
102
+ status: AgentStatus;
103
+ provider: string;
104
+ modelId: string;
105
+ steps: number;
106
+ toolCalls: import("@zhivex-ai/core").ToolCall[];
107
+ childRuns: NonNullable<AgentRunState["childRuns"]>;
108
+ compactions: NonNullable<AgentRunState["compactions"]>;
109
+ usage?: AgentRunState["usage"];
110
+ error?: AgentRunState["error"];
111
+ pendingApprovals: AgentRunState["pendingApprovals"];
112
+ };
113
+ schemaVersion: typeof import("@zhivex-ai/core").AGENT_CONTROL_PLANE_SCHEMA_VERSION;
114
+ type: "agent_run_ledger";
115
+ runId: string;
116
+ agentId?: string;
117
+ provider: string;
118
+ modelId: string;
119
+ status: AgentRunState["status"];
120
+ audit: import("@zhivex-ai/core").AgentAuditRecord;
121
+ toolAudit: import("@zhivex-ai/core").ToolAuditRecord[];
122
+ timeline?: import("@zhivex-ai/core").AgentReplayResult["timeline"];
123
+ trace: import("@zhivex-ai/core").AgentTraceArtifact;
124
+ summary: import("@zhivex-ai/core").AgentTraceSummary;
125
+ cost?: import("@zhivex-ai/core").CostEstimate;
126
+ metadata?: Record<string, import("@zhivex-ai/core").JsonValue>;
127
+ };
128
+ toolJournal: {
129
+ toolCallId: string;
130
+ toolName: string;
131
+ status: import("@zhivex-ai/core").AgentToolCallJournalStatus;
132
+ revision: number;
133
+ startedAt: number | undefined;
134
+ completedAt: number | undefined;
135
+ updatedAt: number;
136
+ error: {
137
+ message: string;
138
+ } | undefined;
139
+ }[];
140
+ }>;
141
+ export declare const cancelHarnessRun: (store: AgentRunStore, config: HarnessConfig, runId: string, options?: {
142
+ reason?: string;
143
+ cascade?: boolean;
144
+ final?: boolean;
145
+ }) => Promise<{
146
+ parent: {
147
+ runId: string;
148
+ revision: number;
149
+ status: AgentStatus;
150
+ provider: string;
151
+ model: string;
152
+ agentId: string | undefined;
153
+ parentRunId: string | undefined;
154
+ idempotencyKey: string | undefined;
155
+ scope: import("@zhivex-ai/core").AgentStoreScope | undefined;
156
+ steps: number;
157
+ toolCalls: number;
158
+ toolErrors: number;
159
+ pendingApprovals: number;
160
+ compactions: number;
161
+ childRuns: number;
162
+ usage: import("@zhivex-ai/core").TokenUsage | undefined;
163
+ startedAt: number | undefined;
164
+ updatedAt: number | undefined;
165
+ cancelledAt: number | undefined;
166
+ cancellationReason: string | undefined;
167
+ };
168
+ children: {
169
+ runId: string;
170
+ revision: number;
171
+ status: AgentStatus;
172
+ provider: string;
173
+ model: string;
174
+ agentId: string | undefined;
175
+ parentRunId: string | undefined;
176
+ idempotencyKey: string | undefined;
177
+ scope: import("@zhivex-ai/core").AgentStoreScope | undefined;
178
+ steps: number;
179
+ toolCalls: number;
180
+ toolErrors: number;
181
+ pendingApprovals: number;
182
+ compactions: number;
183
+ childRuns: number;
184
+ usage: import("@zhivex-ai/core").TokenUsage | undefined;
185
+ startedAt: number | undefined;
186
+ updatedAt: number | undefined;
187
+ cancelledAt: number | undefined;
188
+ cancellationReason: string | undefined;
189
+ }[];
190
+ schemaVersion: 1;
191
+ kind: "run-cancellation";
192
+ cascade: boolean;
193
+ } | {
194
+ run: {
195
+ runId: string;
196
+ revision: number;
197
+ status: AgentStatus;
198
+ provider: string;
199
+ model: string;
200
+ agentId: string | undefined;
201
+ parentRunId: string | undefined;
202
+ idempotencyKey: string | undefined;
203
+ scope: import("@zhivex-ai/core").AgentStoreScope | undefined;
204
+ steps: number;
205
+ toolCalls: number;
206
+ toolErrors: number;
207
+ pendingApprovals: number;
208
+ compactions: number;
209
+ childRuns: number;
210
+ usage: import("@zhivex-ai/core").TokenUsage | undefined;
211
+ startedAt: number | undefined;
212
+ updatedAt: number | undefined;
213
+ cancelledAt: number | undefined;
214
+ cancellationReason: string | undefined;
215
+ };
216
+ schemaVersion: 1;
217
+ kind: "run-cancellation";
218
+ cascade: boolean;
219
+ }>;
220
+ export declare const cleanupHarnessRuns: (store: AgentRunStore, config: HarnessConfig, options: {
221
+ before: number;
222
+ statuses?: AgentStatus[];
223
+ limit?: number;
224
+ }) => Promise<{
225
+ schemaVersion: 1;
226
+ kind: "run-cleanup";
227
+ before: number;
228
+ statuses: AgentStatus[];
229
+ deleted: number;
230
+ }>;
231
+ export declare const migrateLegacyFileRuns: (directory: string, target: AgentRunStore, config: HarnessConfig) => Promise<HarnessMigrationResult>;
232
+ export declare const openHarnessPersistence: (config: HarnessConfig, options?: {
233
+ migrateLegacyFileStore?: boolean;
234
+ }) => Promise<HarnessPersistence>;
@@ -0,0 +1,43 @@
1
+ import { runAgentGroup, type AgentDefinition, type AgentHarnessBinding, type AgentRunInput, type AgentSubAgentDefinition, type LanguageModel, type ToolSet } from "@zhivex-ai/agents";
2
+ import type { AgentMemoryStore, AgentRunStore, AgentTelemetryObserver } from "@zhivex-ai/agents/ops";
3
+ import type { HarnessConfig, HarnessSubagentProfile } from "./config.js";
4
+ export interface HarnessSubagentProfileDescriptor {
5
+ id: HarnessSubagentProfile;
6
+ toolName: string;
7
+ description: string;
8
+ instructions: string;
9
+ toolNames: readonly string[] | "all";
10
+ }
11
+ export declare const HARNESS_SUBAGENT_PROFILE_DESCRIPTORS: readonly HarnessSubagentProfileDescriptor[];
12
+ export interface HarnessSubagentRuntime {
13
+ definitions: AgentSubAgentDefinition<LanguageModel>[];
14
+ agents: ReadonlyMap<HarnessSubagentProfile, AgentDefinition<LanguageModel>>;
15
+ }
16
+ export declare const createHarnessSubagents: (options: {
17
+ config: HarnessConfig;
18
+ parentBinding: AgentHarnessBinding;
19
+ model: LanguageModel;
20
+ models?: Partial<Record<HarnessSubagentProfile, LanguageModel>>;
21
+ tools: ToolSet;
22
+ store: AgentRunStore;
23
+ memory?: AgentMemoryStore;
24
+ onTelemetryEvent?: AgentTelemetryObserver;
25
+ }) => HarnessSubagentRuntime;
26
+ export interface HarnessReviewGroupResult {
27
+ schemaVersion: 1;
28
+ kind: "review-group";
29
+ groupId: string;
30
+ status: "completed" | "failed";
31
+ profiles: HarnessSubagentProfile[];
32
+ outputs: Awaited<ReturnType<typeof runAgentGroup>>["outputs"];
33
+ }
34
+ export declare const runHarnessReviewGroup: (runtime: {
35
+ config: HarnessConfig;
36
+ subagents: ReadonlyMap<HarnessSubagentProfile, AgentDefinition<LanguageModel>>;
37
+ }, input: {
38
+ prompt: string;
39
+ scope?: AgentRunInput<LanguageModel>["scope"];
40
+ abortSignal?: AbortSignal;
41
+ metadata?: AgentRunInput<LanguageModel>["metadata"];
42
+ groupId?: string;
43
+ }, profiles?: readonly HarnessSubagentProfile[]) => Promise<HarnessReviewGroupResult>;
@@ -0,0 +1 @@
1
+ export declare const validateStateDirectory: (workspace: string, stateDirectory: string) => Promise<void>;
@@ -0,0 +1,4 @@
1
+ /** The application version. package.json is the single source of truth. */
2
+ export declare const HARNESS_VERSION: string;
3
+ /** The Bun compatibility range declared by the package. */
4
+ export declare const BUN_ENGINE_RANGE: string;
@@ -0,0 +1,90 @@
1
+ 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";
2
+ export type HarnessCheck = string;
3
+ export interface WorkspaceFile {
4
+ path: string;
5
+ size: number;
6
+ digest: FileDigest;
7
+ }
8
+ export interface SearchMatch {
9
+ path: string;
10
+ line: number;
11
+ text: string;
12
+ digest: FileDigest;
13
+ }
14
+ export interface ListFilesOptions {
15
+ limit?: number;
16
+ cursor?: string;
17
+ }
18
+ export interface SearchFilesOptions {
19
+ caseSensitive?: boolean;
20
+ limit?: number;
21
+ cursor?: string;
22
+ /** @deprecated Use limit. */
23
+ maxMatches?: number;
24
+ }
25
+ export interface CommandResult {
26
+ command: string[];
27
+ exitCode: number;
28
+ stdout: string;
29
+ stderr: string;
30
+ timedOut: boolean;
31
+ }
32
+ export declare class Workspace {
33
+ readonly root: string;
34
+ private readonly auditEntries;
35
+ private constructor();
36
+ static open(root: string): Promise<Workspace>;
37
+ private lexicalPath;
38
+ private safePath;
39
+ private readStableFile;
40
+ private rulesForDirectory;
41
+ private collectFiles;
42
+ listFiles(relativePath?: string, options?: number | ListFilesOptions): Promise<{
43
+ nextCursor?: string;
44
+ files: WorkspaceFile[];
45
+ truncated: boolean;
46
+ }>;
47
+ readFile(relativePath: string, startLine?: number, endLine?: number): Promise<{
48
+ path: string;
49
+ digest: string;
50
+ startLine: number;
51
+ endLine: number;
52
+ totalLines: number;
53
+ content: string;
54
+ truncated: boolean;
55
+ }>;
56
+ searchFiles(query: string, relativePath?: string, options?: SearchFilesOptions): Promise<{
57
+ nextCursor?: string;
58
+ matches: SearchMatch[];
59
+ truncated: boolean;
60
+ }>;
61
+ private audit;
62
+ mutationAudit(): readonly MutationAuditEntry[];
63
+ private secureWorkspaceDirectory;
64
+ private stageFile;
65
+ applyPatch(input: ApplyEditProposalInput): Promise<ApplyPatchResult>;
66
+ /** @deprecated Use applyPatch with an inspected expectedDigest. */
67
+ writeFile(relativePath: string, content: string, overwrite?: boolean): Promise<{
68
+ path: string;
69
+ bytes: number;
70
+ overwritten: boolean;
71
+ }>;
72
+ /** @deprecated Use applyPatch with an inspected expectedDigest. */
73
+ replaceInFile(relativePath: string, oldText: string, newText: string): Promise<{
74
+ path: string;
75
+ beforeBytes: number;
76
+ afterBytes: number;
77
+ }>;
78
+ moveFile(input: MoveFileInput): Promise<MoveFileResult>;
79
+ private secureQuarantineDirectory;
80
+ private writeManifest;
81
+ private readInternalRegularFile;
82
+ quarantineFile(input: QuarantineFileInput): Promise<QuarantineFileResult>;
83
+ restoreQuarantined(input: RestoreFileInput): Promise<RestoreFileResult>;
84
+ runCheck(check: HarnessCheck, expectedScript: string, allowedChecks?: readonly string[]): Promise<CommandResult>;
85
+ gitDiff(): Promise<{
86
+ status: CommandResult;
87
+ diff: CommandResult;
88
+ staged: CommandResult;
89
+ }>;
90
+ }
package/docs/CLI.md ADDED
@@ -0,0 +1,65 @@
1
+ # CLI contract
2
+
3
+ Zhivex Harness `0.5.x` is Bun-first and exposes a human terminal interface plus versioned JSON documents for automation.
4
+
5
+ ## Commands
6
+
7
+ ```text
8
+ zhivex-harness run [options] "task"
9
+ zhivex-harness review [options] "review task"
10
+ zhivex-harness chat [options]
11
+ zhivex-harness providers [--json]
12
+ zhivex-harness doctor [options] [--json]
13
+ zhivex-harness resume [options] <runId> --approve|--deny
14
+ zhivex-harness runs list [--status <status>] [--limit <n>] [--cursor <cursor>]
15
+ zhivex-harness runs inspect <runId>
16
+ zhivex-harness runs export <runId>
17
+ zhivex-harness runs cancel <runId> [--reason <text>] [--cascade] [--final]
18
+ zhivex-harness runs cleanup --before <date|timestamp> [--status <status>] [--limit <n>]
19
+ zhivex-harness --version
20
+ zhivex-harness --help
21
+ ```
22
+
23
+ `doctor` is local and makes no provider or MCP request. It checks the Bun version, workspace, Git, supported package scripts, state-directory safety, provider credential presence, endpoint shape, provider configuration, and the local MCP configuration file without returning secret or endpoint values.
24
+
25
+ `--allow-check <script>` is repeatable and replaces the default check allowlist for that invocation. Values are declared `package.json` script names, never command text.
26
+
27
+ `--require-capability <name>`, `--subagent <profile>`, and `--reviewer <explorer|reviewer>` are repeatable. `--mcp-config <path>` loads a schema-versioned file inside the canonical workspace. Child limits use `--subagent-max-steps`, `--subagent-max-tool-calls`, `--subagent-max-tool-errors`, `--subagent-max-input-tokens`, `--subagent-max-output-tokens`, `--subagent-max-total-tokens`, and `--subagent-timeout-ms`. Parallel review is capped by `--max-parallel-reviews`.
28
+
29
+ `review` is application-owned parallelism and accepts only read-only explorer/reviewer members. Model-directed delegation occurs only inside `run` or `chat` when the parent invokes an enabled `delegate_<profile>` tool.
30
+
31
+ ## Exit codes
32
+
33
+ | Code | Meaning |
34
+ | --- | --- |
35
+ | `0` | Command completed successfully. |
36
+ | `1` | Runtime, provider, or agent failure. |
37
+ | `2` | Invalid CLI usage. |
38
+ | `3` | `doctor` found a blocking configuration problem. |
39
+
40
+ Agent results with status `failed` or `timed_out` return `1`. A paused approval is a valid durable result and does not imply a runtime failure.
41
+
42
+ Operator commands do not construct a provider model and do not require provider credentials. They must use the workspace, state directory, backend, and scope that own the target run. `cancel` creates a cooperative cancellation request by default; `--final` writes a terminal cancellation. `cleanup` requires an explicit cutoff and defaults to terminal statuses only.
43
+
44
+ ## JSON schemas
45
+
46
+ All structured documents include:
47
+
48
+ ```json
49
+ {
50
+ "schemaVersion": 1,
51
+ "kind": "providers | doctor | run-result | review-group | run-list | run-inspection | run-export | run-cancellation | run-cleanup"
52
+ }
53
+ ```
54
+
55
+ Additive fields may appear within schema version `1`. Removing a field, changing its meaning, or changing a field type requires a new schema version and a migration note. Human-readable output is not a machine contract.
56
+
57
+ Provider diagnostics include credential variable names and boolean presence only. Endpoint diagnostics include validation booleans only. Neither contract contains credential values or endpoint URLs.
58
+
59
+ `run-result.mutations` contains the mutation audit entries produced by the current harness process. `run-result.children` reports bounded child identity, status, steps, tool/error counts, and usage without raw child messages or tool payloads. Capability evidence and configured MCP server names are included. `review-group` contains a group ID and one bounded member result. Operational inspect/export documents are redacted and do not include raw messages, tool inputs/outputs, approval arguments, metadata, or full output text; `run-inspection.hierarchy` adds the redacted run tree. Tool-level repository-editing documents use their own schema version `1` and kinds such as `edit-proposal`, `patch-result`, `mutation-audit`, and `workspace-diff`; see [REPOSITORY_EDITING.md](./REPOSITORY_EDITING.md).
60
+
61
+ ## Configuration schema
62
+
63
+ Resolved library configuration includes `schemaVersion: 3`, an explicit `allowedChecks` array, `storeBackend`, `scope`, parent `budget`, `compaction`, `requiredCapabilities`, optional `mcpConfigPath`, and an `orchestration` object with profiles, child budget/timeout, and review concurrency. The check default is `test`, `typecheck`, `lint`, and `build`; `--allow-check` or `ZHIVEX_HARNESS_ALLOWED_CHECKS` replaces that set. Passing a different explicit schema version fails before a model or tool can run. During `0.x`, a minor release may add a new schema version with a documented migration; patch releases remain compatible.
64
+
65
+ The default state directory is `<workspace>/.zhivex-harness/runs` and the default backend is scoped SQLite at `operations.sqlite`. Explicit external state directories are supported, but the workspace root, filesystem root, sensitive workspace paths, regular files, and symbolic-link targets are rejected before the run store is created. See [DURABLE_OPERATIONS.md](./DURABLE_OPERATIONS.md) for state migration and operations, and [EXTENSIBILITY.md](./EXTENSIBILITY.md) for capability, MCP, subagent, and review-group configuration.
@@ -0,0 +1,116 @@
1
+ # Durable operations
2
+
3
+ Zhivex Harness `0.4.x` makes local runs inspectable and recoverable across process restarts. The default store is Bun SQLite at `<workspace>/.zhivex-harness/runs/operations.sqlite`; `--store file` remains a migration fallback.
4
+
5
+ ## Scope and identity
6
+
7
+ Every durable key is isolated by:
8
+
9
+ - tenant: `local` by default;
10
+ - optional user;
11
+ - namespace: a SHA-256-derived canonical-workspace identifier by default.
12
+
13
+ Use the same workspace, state directory, backend, tenant, user, and namespace for `run`, `resume`, and every `runs` command. A run fingerprint is bound to the canonical workspace, durable scope, provider/model, tool contract, approval policy, config schema, and harness version. Resume fails closed when a current binding differs.
14
+
15
+ ```bash
16
+ zhivex-harness run \
17
+ --tenant acme \
18
+ --user operator-7 \
19
+ --namespace payments \
20
+ --idempotency-key incident-482 \
21
+ "diagnose and repair the regression"
22
+ ```
23
+
24
+ An idempotency key atomically reserves one run inside its scope. Repeating the request returns the existing durable state instead of starting another side-effecting run. SQLite leases and compare-and-swap revisions prevent concurrent workers from silently owning the same checkpoint, while the tool journal records exactly-once claims for side effects.
25
+
26
+ ## Operator commands
27
+
28
+ Operator commands open the store directly. They do not create a provider model and do not require provider credentials.
29
+
30
+ ```bash
31
+ zhivex-harness runs list --status waiting_approval --limit 50
32
+ zhivex-harness runs list --cursor <opaque-cursor> --json
33
+ zhivex-harness runs inspect <runId> --json
34
+ zhivex-harness runs export <runId> --json
35
+ zhivex-harness runs cancel <runId> --reason "superseded"
36
+ zhivex-harness runs cancel <runId> --cascade --final
37
+ zhivex-harness runs cleanup --before 2026-08-01T00:00:00Z
38
+ ```
39
+
40
+ `cancel` records a cooperative `cancel_requested` state by default. Use `--final` only when the operator intends to make cancellation terminal. `--cascade` includes known child runs. `cleanup` requires an ISO-8601 date or millisecond timestamp; without `--status`, it removes only `completed`, `failed`, `cancelled`, and `timed_out` runs. The default maximum cleanup batch is 1,000.
41
+
42
+ `inspect` and `export` share the same redacted operational artifact. Export changes only the document kind to `run-export`; it does not create an unredacted archive on disk.
43
+
44
+ ## Budgets
45
+
46
+ Default limits are:
47
+
48
+ | Budget | Default |
49
+ | --- | ---: |
50
+ | Steps | 12 |
51
+ | Wall clock | 900,000 ms |
52
+ | Tool calls | 32 |
53
+ | Tool errors | 4 |
54
+ | Input tokens | 100,000 |
55
+ | Output tokens | 30,000 |
56
+ | Total tokens | 120,000 |
57
+
58
+ Configure them with `--max-steps`, `--timeout-ms`, `--max-tool-calls`, `--max-tool-errors`, `--max-input-tokens`, `--max-output-tokens`, and `--max-total-tokens`. Production guardrails stop the run and persist a clear termination reason when a measured limit is reached. Meta and OpenAI receive a compatible output ceiling. Qwen Responses cannot accept `maxTokens`, so its token limits are checked durably before and after a provider step; one step can cross a measured token limit before the output guard observes it.
59
+
60
+ Cost is optional because provider/model pricing is not inferred:
61
+
62
+ ```bash
63
+ zhivex-harness run \
64
+ --max-cost-usd 0.50 \
65
+ --input-cost-per-million 2.50 \
66
+ --output-cost-per-million 10 \
67
+ "review the repository"
68
+ ```
69
+
70
+ At least one pricing value is required with `--max-cost-usd`. Missing input or output pricing is treated as zero. Cost uses provider-reported token usage and operator-supplied prices; it is not a billing record and a single provider step can cross the ceiling before the post-step guardrail observes it.
71
+
72
+ ## Context compaction
73
+
74
+ The default compactor activates at 60 messages or an estimated 40,000 input tokens, retains the 12 most recent messages, and records each compaction durably. The summary includes roles, bounded text, and tool names rather than raw tool payloads. Library users can configure `compactionMaxMessages`, `compactionMaxEstimatedInputTokens`, and `compactionKeepRecentMessages`; corresponding environment variables are listed below.
75
+
76
+ ## Redaction and exports
77
+
78
+ Operational artifacts are schema version `1`. By default they exclude raw messages, tool inputs, tool outputs, approval arguments, run metadata, and full output text. Traces, ledger previews, and journal errors apply the production secret/email redaction policy. This is a defensive export boundary, not a guarantee that arbitrary model-generated prose can never contain sensitive business data; control prompt contents and workspace permissions accordingly.
79
+
80
+ ## Migration from 0.3.x
81
+
82
+ Opening either backend under the default local/workspace scope scans legacy unscoped file-backed runs in the selected state directory. Runs absent from the scoped store are copied with their tool journals and marked `metadata.migratedFrom: "0.3-file-store"`. With the default backend the target is SQLite; `--store file` creates scoped file copies as a temporary compatibility path. Migration is idempotent and retains the unscoped source files for rollback; it does not delete or rewrite the legacy state. Custom tenant, user, or namespace scopes never import unscoped legacy data implicitly; library operators must call `migrateLegacyFileRuns` intentionally with the target scoped store.
83
+
84
+ Legacy paused approvals can be resumed explicitly. The harness writes the current binding before continuing, so subsequent resumes are protected by the `0.4.x` fingerprint. Review the pending approval before accepting it. If migration must be deferred, use `--store file`; do not point SQLite and file workers at the same logical request concurrently.
85
+
86
+ The SQLite state directory is owner-only and the database file is owner-readable/writable. Symlinked state directories and database files are rejected. Back up the state directory before moving scopes or performing manual database maintenance.
87
+
88
+ ## Environment variables
89
+
90
+ CLI flags take precedence over these variables:
91
+
92
+ ```text
93
+ ZHIVEX_HARNESS_STORE
94
+ ZHIVEX_HARNESS_TENANT_ID
95
+ ZHIVEX_HARNESS_USER_ID
96
+ ZHIVEX_HARNESS_NAMESPACE
97
+ ZHIVEX_HARNESS_MAX_STEPS
98
+ ZHIVEX_HARNESS_TIMEOUT_MS
99
+ ZHIVEX_HARNESS_MAX_TOOL_CALLS
100
+ ZHIVEX_HARNESS_MAX_TOOL_ERRORS
101
+ ZHIVEX_HARNESS_MAX_INPUT_TOKENS
102
+ ZHIVEX_HARNESS_MAX_OUTPUT_TOKENS
103
+ ZHIVEX_HARNESS_MAX_TOTAL_TOKENS
104
+ ZHIVEX_HARNESS_MAX_COST_USD
105
+ ZHIVEX_HARNESS_INPUT_COST_PER_MILLION
106
+ ZHIVEX_HARNESS_OUTPUT_COST_PER_MILLION
107
+ ZHIVEX_HARNESS_COMPACTION_MAX_MESSAGES
108
+ ZHIVEX_HARNESS_COMPACTION_MAX_INPUT_TOKENS
109
+ ZHIVEX_HARNESS_COMPACTION_KEEP_RECENT
110
+ ```
111
+
112
+ ## Evaluation gate
113
+
114
+ `bun run evaluate` executes five deterministic golden cases: analysis-only, approved edit-and-test, denied approval, SQLite restart recovery, and provider switching. It checks terminal status, exact tool sequence, maximum steps, a 30-second per-case latency bound, denied-write safety, and exactly-once recovery. `bun run check` runs this gate before the installed-tarball smoke.
115
+
116
+ The golden baseline is packaged at `evaluations/golden-expectations.json`. It is regression evidence, not live-provider certification. Provider behavior must still pass the opt-in, credentialed live gate described in [LIVE_CERTIFICATION.md](./LIVE_CERTIFICATION.md).