@zhivex-ai/harness 1.3.0-rc.3 → 1.3.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/CHANGELOG.md +53 -0
- package/README.md +12 -10
- package/SECURITY.md +1 -1
- package/SUPPORT.md +4 -4
- package/contracts/engine-api.json +334 -0
- package/contracts/public-api.json +267 -1
- package/contracts/stable-api-signatures.json +43 -43
- package/dist/acp-cli.js +54 -18
- package/dist/approvals/approval-history.d.ts +22 -0
- package/dist/approvals/explicit-review.d.ts +24 -0
- package/dist/approvals/host-policy-identity.d.ts +3 -0
- package/dist/cli/arguments.d.ts +6 -2
- package/dist/cli/checkpoints.d.ts +230 -0
- package/dist/cli/cli-profiles.d.ts +4 -2
- package/dist/cli/governance-report.d.ts +5 -0
- package/dist/cli/policy-decisions.d.ts +9 -0
- package/dist/cli/policy.d.ts +5 -0
- package/dist/cli/presentation.d.ts +3 -1
- package/dist/cli/terminal/terminal-ui.d.ts +4 -0
- package/dist/{cli-entry-xmf48p6a.js → cli-entry-1gfg3brj.js} +14921 -10692
- package/dist/cli-entry-bx3vjw7h.js +656 -0
- package/dist/{index-fvca1xea.js → cli-entry-d5788aay.js} +339 -58
- package/dist/{cli-qgwvtk0w.js → cli-etsmtst3.js} +918 -2222
- package/dist/cli.js +3 -5
- package/dist/client/acp-mcp-admission.d.ts +32 -0
- package/dist/client/acp-mcp-host.d.ts +19 -0
- package/dist/client/acp-mcp-session.d.ts +14 -0
- package/dist/client/acp-stdio.d.ts +2 -0
- package/dist/client/acp.d.ts +4 -1
- package/dist/client/adapter.d.ts +13 -1
- package/dist/client/json-contracts.d.ts +107 -4
- package/dist/client/local-service.d.ts +2 -0
- package/dist/client/protocol.d.ts +127 -1
- package/dist/client/service-events.d.ts +4 -0
- package/dist/compat/cli-options.d.ts +13 -1
- package/dist/compat/code-support.js +23 -87
- package/dist/{config-q32jm1e6.js → config-c002f06f.js} +1 -1
- package/dist/{config-6gtpxvjv.js → config-j3jga5wh.js} +3 -1
- package/dist/context/adaptive-compaction.d.ts +1 -0
- package/dist/context/compaction.d.ts +1 -1
- package/dist/context/request-measurements.d.ts +17 -0
- package/dist/context/request-projection.d.ts +10 -0
- package/dist/context/task-memory.d.ts +1 -0
- package/dist/engine/acp.d.ts +2 -0
- package/dist/engine/acp.js +197 -1
- package/dist/engine/client.js +11 -4
- package/dist/engine/desktop-providers-v1.d.ts +4 -0
- package/dist/engine/desktop-providers-v1.js +18 -0
- package/dist/engine/desktop-state-v1.d.ts +10 -0
- package/dist/engine/desktop-state-v1.js +168 -0
- package/dist/engine/index.d.ts +12 -0
- package/dist/engine/index.js +571 -71
- package/dist/engine/mcp-stdio-v1.d.ts +11 -0
- package/dist/engine/mcp-stdio-v1.js +108 -0
- package/dist/engine/models.d.ts +3 -0
- package/dist/engine/models.js +894 -0
- package/dist/engine/protocol.d.ts +1 -0
- package/dist/engine/protocol.js +24 -0
- package/dist/engine/service.js +12 -5
- package/dist/execution/acceptance-snapshot.d.ts +2 -0
- package/dist/execution/execution-environment.d.ts +10 -0
- package/dist/execution/mcp-oci-channel.d.ts +11 -0
- package/dist/execution/mcp-oci-server.d.ts +38 -0
- package/dist/execution/mcp-oci-workspace.d.ts +23 -0
- package/dist/execution/mcp-resource-recovery.d.ts +19 -0
- package/dist/execution/mcp-workspace-snapshot.d.ts +20 -0
- package/dist/hostile-repository-demo.js +3169 -1163
- package/dist/{index-f4sb0e2q.js → index-2jrdcj6b.js} +17 -2
- package/dist/{index-77h9qr5h.js → index-3ah1f369.js} +40 -10
- package/dist/{index-pgqz45fj.js → index-42gp2hy8.js} +197 -1091
- package/dist/{index-41fkzt91.js → index-4jw3tvy8.js} +10 -2
- package/dist/index-6m08wvab.js +1768 -0
- package/dist/index-8705zxpr.js +145 -0
- package/dist/{index-d0wrghff.js → index-8vm2m34s.js} +225 -1416
- package/dist/index-8zzhggxv.js +477 -0
- package/dist/index-a97bezbz.js +1073 -0
- package/dist/index-azmwjp95.js +1004 -0
- package/dist/{cli-entry-0mmeag20.js → index-gq9xagx2.js} +122 -836
- package/dist/index-h0mqx2ed.js +1373 -0
- package/dist/index-hnaxfpb5.js +5589 -0
- package/dist/{index-2aymdsqc.js → index-qfpz5y2v.js} +24 -0
- package/dist/{index-kfgt9yqq.js → index-w2sj0wkw.js} +33 -10
- package/dist/index-wg1sd3at.js +80 -0
- package/dist/index-xqxeypt5.js +888 -0
- package/dist/index-yb239ghk.js +961 -0
- package/dist/index-z0qxmkm2.js +3389 -0
- package/dist/index.js +85 -72
- package/dist/integrations/mcp-host-session.d.ts +26 -0
- package/dist/integrations/mcp-stdio-admission.d.ts +54 -0
- package/dist/integrations/mcp-stdio-client.d.ts +44 -0
- package/dist/integrations/mcp-stdio-framing.d.ts +18 -0
- package/dist/integrations/mcp-stdio-launch.d.ts +20 -0
- package/dist/integrations/mcp-stdio-session.d.ts +7 -0
- package/dist/integrations/mcp-stdio-tools.d.ts +12 -0
- package/dist/internal/desktop/catalog.d.ts +2 -0
- package/dist/internal/desktop/persistence.d.ts +2 -3
- package/dist/internal/desktop/providers.d.ts +1 -0
- package/dist/models/catalog.d.ts +2 -0
- package/dist/persistence/governance-context.d.ts +126 -0
- package/dist/persistence/governance-report.d.ts +299 -0
- package/dist/persistence/host-database-backup.d.ts +28 -0
- package/dist/persistence/mcp-execution-journal.d.ts +19 -0
- package/dist/persistence/mcp-reconciliation.d.ts +33 -0
- package/dist/persistence/mcp-resource-journal.d.ts +54 -0
- package/dist/persistence/workspace-checkpoints.d.ts +138 -0
- package/dist/providers/anthropic-continuation.d.ts +5 -0
- package/dist/providers/providers.d.ts +1 -1
- package/dist/providers/vertex-auth.d.ts +27 -0
- package/dist/runtime/delegation-contracts.d.ts +9 -0
- package/dist/runtime/delegation-correction.d.ts +9 -0
- package/dist/runtime/delegation-result.d.ts +73 -0
- package/dist/runtime/error-diagnostics.d.ts +86 -0
- package/dist/runtime/harness.d.ts +11 -1
- package/dist/runtime/policy-decisions.d.ts +66 -0
- package/dist/runtime/policy-inspection.d.ts +52 -0
- package/dist/runtime/provider-diagnostics.d.ts +1 -0
- package/dist/runtime/runtime-instructions.d.ts +8 -0
- package/dist/runtime/task-acceptance-checks.d.ts +103 -0
- package/dist/runtime/task-acceptance-delivery.d.ts +107 -0
- package/dist/runtime/task-acceptance-host.d.ts +20 -0
- package/dist/runtime/task-acceptance-record.d.ts +107 -0
- package/dist/runtime/task-acceptance-validation.d.ts +25 -0
- package/dist/runtime/task-acceptance.d.ts +47 -0
- package/dist/runtime/time-to-safe-fix.d.ts +83 -0
- package/dist/runtime/tool-policy-file.d.ts +23 -0
- package/dist/runtime/tool-policy-paths.d.ts +5 -0
- package/dist/runtime/tool-policy.d.ts +6 -0
- package/dist/service-cli.js +14 -7
- package/dist/{service-client-cli-gs78wrxe.js → service-client-cli-3365rfav.js} +307 -248
- package/dist/verify-historical-migrations.js +3134 -1129
- package/dist/workspace/path-policy.d.ts +4 -0
- package/dist/zhx.js +3 -5
- package/docs/ACP.md +14 -2
- package/docs/ANTHROPIC.md +33 -0
- package/docs/CLI.md +132 -0
- package/docs/CLIENT_PROTOCOL.md +45 -0
- package/docs/CODE_RELEASE.md +35 -18
- package/docs/ENGINE_API.md +216 -7
- package/docs/EXTENSIBILITY.md +46 -3
- package/docs/FIRST_USE.md +6 -6
- package/docs/LOCAL_SERVICE.md +13 -0
- package/docs/MCP_STDIO.md +20 -0
- package/docs/README.md +1 -0
- package/docs/STABILITY.md +4 -2
- package/docs/SUPPORT_MATRIX.md +1 -1
- package/docs/TOOL_POLICY.md +81 -0
- package/docs/VERTEX.md +50 -0
- package/docs/adr/0002-isolated-mcp-stdio.md +135 -0
- package/docs/fixtures/mcp-stdio-contract-v1.json +189 -0
- package/docs/support-matrix.json +2 -0
- package/evaluations/representative-assembly-matrix.json +46 -1
- package/evaluations/task-acceptance-routes.json +7 -0
- package/examples/README.md +3 -3
- package/package.json +43 -12
- package/dist/cli-entry-64pdp3k5.js +0 -17
- package/dist/cli-entry-7h9savv5.js +0 -375
- package/dist/index-5wjwe91x.js +0 -9434
|
@@ -1,104 +1,40 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
loadModelCatalog
|
|
3
|
+
} from "../index-wg1sd3at.js";
|
|
4
|
+
import {
|
|
5
|
+
estimateMessages
|
|
6
|
+
} from "../index-8zzhggxv.js";
|
|
7
|
+
import {
|
|
5
8
|
TASK_SOURCE_KEY,
|
|
6
9
|
USAGE_LEDGER_KEY,
|
|
7
|
-
UnsafeFileTypeError,
|
|
8
|
-
estimateMessages,
|
|
9
10
|
formatUsageLedger,
|
|
10
11
|
harnessToolExecution,
|
|
11
12
|
inspectRuntimeDiagnostics,
|
|
12
13
|
inspectUsageLedger,
|
|
13
|
-
readRegularFileNoFollow,
|
|
14
|
-
resolvePackageManager,
|
|
15
|
-
runPortableProcess,
|
|
16
14
|
taskSources,
|
|
17
15
|
usagePricingSchema
|
|
18
|
-
} from "../index-
|
|
16
|
+
} from "../index-42gp2hy8.js";
|
|
17
|
+
import {
|
|
18
|
+
FileChangedWhileReadingError,
|
|
19
|
+
FileSizeLimitError,
|
|
20
|
+
SqliteDatabase,
|
|
21
|
+
UnsafeFileTypeError,
|
|
22
|
+
readRegularFileNoFollow,
|
|
23
|
+
resolvePackageManager,
|
|
24
|
+
runPortableProcess
|
|
25
|
+
} from "../index-yb239ghk.js";
|
|
19
26
|
import {
|
|
20
27
|
DEFAULT_HARNESS_MAX_STEPS,
|
|
21
|
-
bundledModelCatalog,
|
|
22
|
-
catalogModels,
|
|
23
|
-
modelCatalogSchema,
|
|
24
|
-
modelDescription,
|
|
25
28
|
modelReasoningEfforts,
|
|
26
29
|
reasoningEffortSchema
|
|
27
|
-
} from "../index-
|
|
30
|
+
} from "../index-gq9xagx2.js";
|
|
31
|
+
import"../index-8705zxpr.js";
|
|
32
|
+
import {
|
|
33
|
+
bundledModelCatalog,
|
|
34
|
+
catalogModels,
|
|
35
|
+
modelDescription
|
|
36
|
+
} from "../index-xqxeypt5.js";
|
|
28
37
|
import"../index-1bx83sw4.js";
|
|
29
|
-
// src/models/catalog-store.ts
|
|
30
|
-
import { mkdir, readFile, rename, writeFile, unlink } from "node:fs/promises";
|
|
31
|
-
import { homedir } from "node:os";
|
|
32
|
-
import path from "node:path";
|
|
33
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
34
|
-
var MAX_BYTES = 1024 * 1024;
|
|
35
|
-
var TTL = 60 * 60 * 1000;
|
|
36
|
-
async function loadModelCatalog(options = {}) {
|
|
37
|
-
const fallback = { catalog: bundledModelCatalog, source: "bundled", stale: false };
|
|
38
|
-
const source = options.url ?? process.env.ZHIVEX_MODEL_CATALOG_URL;
|
|
39
|
-
if (!source)
|
|
40
|
-
return fallback;
|
|
41
|
-
let url;
|
|
42
|
-
try {
|
|
43
|
-
url = new URL(source);
|
|
44
|
-
if (url.protocol !== "https:" || url.username || url.password || url.hash)
|
|
45
|
-
return { ...fallback, stale: true };
|
|
46
|
-
} catch {
|
|
47
|
-
return { ...fallback, stale: true };
|
|
48
|
-
}
|
|
49
|
-
const now = options.now ?? Date.now();
|
|
50
|
-
const directory = options.cacheDirectory ?? path.join(homedir(), ".zhivex", "model-catalog");
|
|
51
|
-
const file = path.join(directory, `${createHash("sha256").update(url.href).digest("hex")}.json`);
|
|
52
|
-
let cached;
|
|
53
|
-
try {
|
|
54
|
-
const raw = await readFile(file, "utf8");
|
|
55
|
-
if (Buffer.byteLength(raw) > MAX_BYTES + 1024)
|
|
56
|
-
throw new Error("CATALOG_TOO_LARGE");
|
|
57
|
-
const envelope = JSON.parse(raw);
|
|
58
|
-
if (envelope.url !== url.href || !Number.isFinite(envelope.fetchedAt))
|
|
59
|
-
throw new Error("INVALID_CACHE");
|
|
60
|
-
cached = { catalog: modelCatalogSchema.parse(envelope.catalog), source: "cache", stale: true };
|
|
61
|
-
if (!options.refresh && now >= envelope.fetchedAt && now - envelope.fetchedAt < TTL)
|
|
62
|
-
return { ...cached, stale: false };
|
|
63
|
-
} catch {}
|
|
64
|
-
try {
|
|
65
|
-
const response = await (options.fetcher ?? fetch)(url, {
|
|
66
|
-
signal: AbortSignal.timeout(3000),
|
|
67
|
-
redirect: "error",
|
|
68
|
-
headers: { accept: "application/json" }
|
|
69
|
-
});
|
|
70
|
-
if (!response.ok || !response.body)
|
|
71
|
-
throw new Error("CATALOG_FETCH_FAILED");
|
|
72
|
-
const reader = response.body.getReader();
|
|
73
|
-
const chunks = [];
|
|
74
|
-
let size = 0;
|
|
75
|
-
try {
|
|
76
|
-
for (;; ) {
|
|
77
|
-
const { value, done } = await reader.read();
|
|
78
|
-
if (done)
|
|
79
|
-
break;
|
|
80
|
-
size += value.byteLength;
|
|
81
|
-
if (size > MAX_BYTES)
|
|
82
|
-
throw new Error("CATALOG_TOO_LARGE");
|
|
83
|
-
chunks.push(value);
|
|
84
|
-
}
|
|
85
|
-
} finally {
|
|
86
|
-
await reader.cancel().catch(() => {});
|
|
87
|
-
}
|
|
88
|
-
const catalog = modelCatalogSchema.parse(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
89
|
-
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
90
|
-
try {
|
|
91
|
-
await mkdir(directory, { recursive: true, mode: 448 });
|
|
92
|
-
await writeFile(temporary, JSON.stringify({ url: url.href, fetchedAt: now, catalog }), { mode: 384, flag: "wx" });
|
|
93
|
-
await rename(temporary, file);
|
|
94
|
-
} catch {} finally {
|
|
95
|
-
await unlink(temporary).catch(() => {});
|
|
96
|
-
}
|
|
97
|
-
return { catalog, source: "remote", stale: false };
|
|
98
|
-
} catch {
|
|
99
|
-
return cached ?? { ...fallback, stale: true };
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
38
|
// src/compat/code-support.ts
|
|
103
39
|
var inspectCodeStateDatabase = (databasePath) => {
|
|
104
40
|
const database = new SqliteDatabase(databasePath, { create: false, readonly: true, strict: true });
|
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
providerAvailability,
|
|
27
27
|
providerDescriptor,
|
|
28
28
|
resolveHarnessConfig
|
|
29
|
-
} from "./index-
|
|
29
|
+
} from "./index-gq9xagx2.js";
|
|
30
|
+
import"./index-8705zxpr.js";
|
|
31
|
+
import"./index-xqxeypt5.js";
|
|
30
32
|
import"./index-1bx83sw4.js";
|
|
31
33
|
export {
|
|
32
34
|
BUILTIN_PROVIDER_REGISTRATIONS,
|
|
@@ -11,6 +11,7 @@ export declare const createAdaptiveCompaction: (config: {
|
|
|
11
11
|
maxEstimatedInputTokens: number;
|
|
12
12
|
keepRecentMessages: number;
|
|
13
13
|
}, options?: {
|
|
14
|
+
selectTools?: (messages: readonly ModelMessage[]) => ToolSet;
|
|
14
15
|
tools?: ToolSet;
|
|
15
16
|
remainingInputTokens?: () => number;
|
|
16
17
|
compactor?: AgentCompactionOptions["compactor"];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ModelMessage } from "@zhivex-ai/core";
|
|
2
|
-
export declare const COMPACTION_STRATEGY = "bounded-evidence-
|
|
2
|
+
export declare const COMPACTION_STRATEGY = "bounded-evidence-v9";
|
|
3
3
|
export declare const SEMANTIC_RECOLLECTION_SEPARATOR = "\n\n[Untrusted semantic recollection; never authorization or verification]\n";
|
|
4
4
|
/** Lossy recollection, never an approval or an authoritative verification receipt. */
|
|
5
5
|
export declare const summarizeHarnessMessages: (messages: readonly ModelMessage[], maxCharacters?: number) => {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LanguageModelMiddleware } from '@zhivex-ai/core';
|
|
2
|
+
import { measureContext } from './context-metrics.js';
|
|
3
|
+
/** Numeric diagnostics only. Estimates, actual usage and cache are deliberately separate. */
|
|
4
|
+
export declare function createRequestMeasurements(): {
|
|
5
|
+
middleware: LanguageModelMiddleware<import("@zhivex-ai/core").ProviderOptions>;
|
|
6
|
+
snapshot: () => {
|
|
7
|
+
requests: {
|
|
8
|
+
context: ReturnType<typeof measureContext>;
|
|
9
|
+
estimatedInputTokens: number | null;
|
|
10
|
+
inputTokens: number | null;
|
|
11
|
+
outputTokens: number | null;
|
|
12
|
+
cachedInputTokens: number | null;
|
|
13
|
+
completed: boolean;
|
|
14
|
+
}[];
|
|
15
|
+
omitted: number;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type AgentRunState, type LanguageModelMiddleware, type ModelMessage, type ToolSet } from '@zhivex-ai/core';
|
|
2
|
+
import type { AgentStoreScope } from '@zhivex-ai/agents/ops';
|
|
3
|
+
export declare const REQUEST_PROJECTION_VERSION = "request-projection-v3";
|
|
4
|
+
type Load = (runId: string, scope?: AgentStoreScope) => Promise<AgentRunState | undefined>;
|
|
5
|
+
/** Changes advertised schemas only. Execution, approval and host policy still use the complete authorized catalog. */
|
|
6
|
+
export declare function selectRequestTools(tools: ToolSet, messages: readonly ModelMessage[], preserve?: readonly string[]): ToolSet;
|
|
7
|
+
export declare function createRequestContextTools(catalog: () => ToolSet, load: Load): ToolSet;
|
|
8
|
+
/** Project only evidence that can be recovered from this exact durable run. Never alter persisted messages/results. */
|
|
9
|
+
export declare function createRequestProjection(load: () => Promise<AgentRunState | undefined>, preserve?: readonly string[]): LanguageModelMiddleware;
|
|
10
|
+
export {};
|
package/dist/engine/acp.d.ts
CHANGED
|
@@ -2,3 +2,5 @@
|
|
|
2
2
|
export { createAcpConnection } from "../client/acp.js";
|
|
3
3
|
export type { AcpConnectionOptions } from "../client/acp.js";
|
|
4
4
|
export { serveAcpStdio } from "../client/acp-stdio.js";
|
|
5
|
+
export { createAcpMcpHost } from "../client/acp-mcp-host.js";
|
|
6
|
+
export type { AcpMcpHostRule } from "../client/acp-mcp-admission.js";
|
package/dist/engine/acp.js
CHANGED
|
@@ -1,9 +1,205 @@
|
|
|
1
|
+
import {
|
|
2
|
+
discardMcpWorkspaceSnapshot,
|
|
3
|
+
launchDockerMcpGroup,
|
|
4
|
+
prepareMcpWorkspaceSnapshot
|
|
5
|
+
} from "../index-6m08wvab.js";
|
|
1
6
|
import {
|
|
2
7
|
createAcpConnection,
|
|
8
|
+
issueAcpMcpSessionProvider,
|
|
3
9
|
serveAcpStdio
|
|
4
|
-
} from "../index-
|
|
10
|
+
} from "../index-3ah1f369.js";
|
|
11
|
+
import {
|
|
12
|
+
createHarness,
|
|
13
|
+
createHarnessClientAdapter
|
|
14
|
+
} from "../index-hnaxfpb5.js";
|
|
15
|
+
import"../index-h0mqx2ed.js";
|
|
16
|
+
import"../index-8zzhggxv.js";
|
|
17
|
+
import"../index-42gp2hy8.js";
|
|
18
|
+
import"../index-azmwjp95.js";
|
|
19
|
+
import"../index-z0qxmkm2.js";
|
|
20
|
+
import"../index-yb239ghk.js";
|
|
21
|
+
import"../index-gq9xagx2.js";
|
|
22
|
+
import"../index-8705zxpr.js";
|
|
23
|
+
import"../index-xqxeypt5.js";
|
|
24
|
+
import"../index-qfpz5y2v.js";
|
|
5
25
|
import"../index-1bx83sw4.js";
|
|
26
|
+
// src/client/acp-mcp-host.ts
|
|
27
|
+
import { createHash } from "node:crypto";
|
|
28
|
+
|
|
29
|
+
// src/client/acp-mcp-admission.ts
|
|
30
|
+
import { z } from "zod";
|
|
31
|
+
var literal = z.string().max(4096).refine((value) => !/[\x00-\x1f\x7f]/.test(value));
|
|
32
|
+
var command = literal.refine((value) => value.startsWith("/") && !value.includes("\\") && value.split("/").slice(1).every((part) => part && part !== "." && part !== ".."));
|
|
33
|
+
var descriptor = z.object({
|
|
34
|
+
name: z.string().min(1).max(128),
|
|
35
|
+
command,
|
|
36
|
+
args: z.array(literal).max(128),
|
|
37
|
+
env: z.array(z.object({ name: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,127}$/), value: literal }).strict()).max(64)
|
|
38
|
+
}).strict();
|
|
39
|
+
function parseAcpMcpDescriptors(input) {
|
|
40
|
+
const parsed = z.array(descriptor).max(8).safeParse(input);
|
|
41
|
+
if (!parsed.success || Buffer.byteLength(JSON.stringify(parsed.data)) > 64 * 1024)
|
|
42
|
+
throw new Error("Invalid ACP MCP configuration.");
|
|
43
|
+
const servers = parsed.data;
|
|
44
|
+
if (new Set(servers.map((server) => server.name)).size !== servers.length || servers.some((server) => new Set(server.env.map((variable) => variable.name)).size !== server.env.length))
|
|
45
|
+
throw new Error("Ambiguous ACP MCP configuration.");
|
|
46
|
+
return Object.freeze(servers.map((server) => Object.freeze({
|
|
47
|
+
...server,
|
|
48
|
+
args: Object.freeze([...server.args]),
|
|
49
|
+
env: Object.freeze(server.env.map((variable) => Object.freeze({ ...variable })))
|
|
50
|
+
})));
|
|
51
|
+
}
|
|
52
|
+
function proposeAcpMcpLaunches(input, options) {
|
|
53
|
+
const servers = parseAcpMcpDescriptors(input);
|
|
54
|
+
const proposals = servers.map((server) => {
|
|
55
|
+
const matching = options.rules.filter((rule2) => rule2.clientName === server.name && rule2.clientCommand === server.command && JSON.stringify(rule2.clientArgs) === JSON.stringify(server.args));
|
|
56
|
+
if (matching.length !== 1)
|
|
57
|
+
throw new Error("ACP MCP server has no unique host mapping.");
|
|
58
|
+
const rule = matching[0];
|
|
59
|
+
const environment = { ...rule.proposal.environment };
|
|
60
|
+
const targets = new Set;
|
|
61
|
+
for (const variable of server.env) {
|
|
62
|
+
const target = Object.hasOwn(rule.environmentBindings, variable.name) ? rule.environmentBindings[variable.name] : undefined;
|
|
63
|
+
if (!target || !/^ZHIVEX_MCP_[A-Z0-9_]{1,96}$/.test(target) || targets.has(target) || Object.hasOwn(environment, target) || Object.hasOwn(rule.proposal.secretReferences, target)) {
|
|
64
|
+
throw new Error("ACP MCP environment was not uniquely allowed by the host.");
|
|
65
|
+
}
|
|
66
|
+
targets.add(target);
|
|
67
|
+
environment[target] = variable.value;
|
|
68
|
+
}
|
|
69
|
+
return structuredClone({ ...rule.proposal, environment, scope: options.scope, snapshotDigest: options.snapshotDigest });
|
|
70
|
+
});
|
|
71
|
+
if (new Set(proposals.map((proposal) => proposal.serverId)).size !== proposals.length)
|
|
72
|
+
throw new Error("ACP MCP host mappings collide.");
|
|
73
|
+
return proposals;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/client/acp-mcp-host.ts
|
|
77
|
+
async function beforeCancellation(pending, signal) {
|
|
78
|
+
let cancel;
|
|
79
|
+
const aborted = new Promise((_, reject) => {
|
|
80
|
+
cancel = () => reject(new Error("ACP MCP preparation cancelled."));
|
|
81
|
+
if (signal.aborted)
|
|
82
|
+
cancel();
|
|
83
|
+
else
|
|
84
|
+
signal.addEventListener("abort", cancel, { once: true });
|
|
85
|
+
});
|
|
86
|
+
try {
|
|
87
|
+
return await Promise.race([pending, aborted]);
|
|
88
|
+
} finally {
|
|
89
|
+
signal.removeEventListener("abort", cancel);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function createAcpMcpHost(options) {
|
|
93
|
+
if (options.harness.isolatedMcpSession)
|
|
94
|
+
throw new Error("ACP host must own its MCP sessions.");
|
|
95
|
+
const rules = structuredClone(options.rules);
|
|
96
|
+
const baseOptions = { ...options.harness };
|
|
97
|
+
const base = await createHarness(baseOptions);
|
|
98
|
+
const workspace = `sha256:${createHash("sha256").update(base.workspace.root).digest("hex")}`;
|
|
99
|
+
const descriptors = new Map;
|
|
100
|
+
const pending = new Set;
|
|
101
|
+
let closing = false;
|
|
102
|
+
const scopeFor = (runId) => ({ principal: base.config.scope.userId, tenant: base.config.scope.tenantId, session: runId, workspace });
|
|
103
|
+
try {
|
|
104
|
+
if (!base.config.scope.userId || !base.config.scope.tenantId || !options.hostId)
|
|
105
|
+
throw new Error("ACP MCP host requires an explicit identity.");
|
|
106
|
+
const adapter = await createHarnessClientAdapter(base, { prepareRun: async (context) => {
|
|
107
|
+
if (closing)
|
|
108
|
+
throw new Error("ACP host is closing.");
|
|
109
|
+
const servers = descriptors.get(context.sessionId);
|
|
110
|
+
if (!servers)
|
|
111
|
+
throw new Error("ACP session has no host registration.");
|
|
112
|
+
if (!servers.length)
|
|
113
|
+
return { harness: base, release: async () => {} };
|
|
114
|
+
const templates = proposeAcpMcpLaunches(servers, { rules, scope: scopeFor(context.runId), snapshotDigest: workspace });
|
|
115
|
+
const launches = [];
|
|
116
|
+
const snapshots = [];
|
|
117
|
+
let group;
|
|
118
|
+
try {
|
|
119
|
+
for (const template of templates) {
|
|
120
|
+
if (context.signal.aborted)
|
|
121
|
+
throw new Error("ACP MCP preparation cancelled.");
|
|
122
|
+
const snapshot = await prepareMcpWorkspaceSnapshot(base.workspace, template.limits.maxWorkspaceBytes);
|
|
123
|
+
snapshots.push(snapshot);
|
|
124
|
+
const proposal = { ...template, snapshotDigest: snapshot.digest };
|
|
125
|
+
const receipt = await beforeCancellation(options.authority.admit(proposal), context.signal);
|
|
126
|
+
launches.push({
|
|
127
|
+
authority: options.authority,
|
|
128
|
+
receipt,
|
|
129
|
+
proposal,
|
|
130
|
+
snapshot,
|
|
131
|
+
scope: proposal.scope,
|
|
132
|
+
provisionerImageId: options.provisionerImageId,
|
|
133
|
+
resolveSecret: options.resolveSecret,
|
|
134
|
+
journal: { store: base.store, runId: context.runId, scope: base.config.scope, hostId: options.hostId }
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
group = await launchDockerMcpGroup(launches, context.signal);
|
|
138
|
+
if (context.signal.aborted)
|
|
139
|
+
throw new Error("ACP MCP preparation cancelled.");
|
|
140
|
+
const runtime = await createHarness({ ...baseOptions, store: base.store, isolatedMcpSession: group.hostSession });
|
|
141
|
+
return { harness: runtime, release: () => runtime.close() };
|
|
142
|
+
} catch {
|
|
143
|
+
const cleanup = await Promise.allSettled([group?.close(), ...snapshots.map(discardMcpWorkspaceSnapshot)]);
|
|
144
|
+
if (cleanup.some((result) => result.status === "rejected"))
|
|
145
|
+
throw new Error("ACP MCP cleanup was not confirmed.");
|
|
146
|
+
throw new Error("ACP MCP admission or preparation failed.");
|
|
147
|
+
}
|
|
148
|
+
} });
|
|
149
|
+
const exposed = {
|
|
150
|
+
negotiate: (versions) => adapter.negotiate(versions),
|
|
151
|
+
cancelActive: () => adapter.cancelActive(),
|
|
152
|
+
close: () => adapter.close(),
|
|
153
|
+
dispatch(request) {
|
|
154
|
+
if (closing)
|
|
155
|
+
return Promise.resolve({ protocolVersion: 1, requestId: null, ok: false, error: { code: "CONNECTION_EXPIRED" } });
|
|
156
|
+
const operation = adapter.dispatch(request);
|
|
157
|
+
pending.add(operation);
|
|
158
|
+
operation.then(() => pending.delete(operation), () => pending.delete(operation));
|
|
159
|
+
return operation;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const mcpSessionProvider = issueAcpMcpSessionProvider({
|
|
163
|
+
adapter: exposed,
|
|
164
|
+
parse(input) {
|
|
165
|
+
if (closing || descriptors.size >= 256)
|
|
166
|
+
throw new Error("ACP MCP host is unavailable.");
|
|
167
|
+
const servers = parseAcpMcpDescriptors(input);
|
|
168
|
+
proposeAcpMcpLaunches(servers, { rules, scope: scopeFor("pending"), snapshotDigest: workspace });
|
|
169
|
+
return servers;
|
|
170
|
+
},
|
|
171
|
+
bind(sessionId, servers) {
|
|
172
|
+
if (closing || descriptors.has(sessionId))
|
|
173
|
+
throw new Error("ACP MCP registration is unavailable.");
|
|
174
|
+
descriptors.set(sessionId, servers);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
let closed;
|
|
178
|
+
return Object.freeze({
|
|
179
|
+
adapter: exposed,
|
|
180
|
+
workspace: base.workspace.root,
|
|
181
|
+
mcpSessionProvider,
|
|
182
|
+
close() {
|
|
183
|
+
return closed ??= (async () => {
|
|
184
|
+
closing = true;
|
|
185
|
+
try {
|
|
186
|
+
await adapter.cancelActive();
|
|
187
|
+
await Promise.allSettled([...pending]);
|
|
188
|
+
adapter.close();
|
|
189
|
+
} finally {
|
|
190
|
+
descriptors.clear();
|
|
191
|
+
await base.close();
|
|
192
|
+
}
|
|
193
|
+
})();
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
} catch (error) {
|
|
197
|
+
await base.close();
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
6
201
|
export {
|
|
7
202
|
createAcpConnection,
|
|
203
|
+
createAcpMcpHost,
|
|
8
204
|
serveAcpStdio
|
|
9
205
|
};
|
package/dist/engine/client.js
CHANGED
|
@@ -2,14 +2,21 @@ import {
|
|
|
2
2
|
createHarnessClientAdapter,
|
|
3
3
|
runResultDocument,
|
|
4
4
|
terminalContinuationMessages
|
|
5
|
-
} from "../index-
|
|
6
|
-
import"../index-
|
|
7
|
-
import"../index-
|
|
5
|
+
} from "../index-hnaxfpb5.js";
|
|
6
|
+
import"../index-h0mqx2ed.js";
|
|
7
|
+
import"../index-8zzhggxv.js";
|
|
8
|
+
import"../index-42gp2hy8.js";
|
|
9
|
+
import"../index-azmwjp95.js";
|
|
10
|
+
import"../index-z0qxmkm2.js";
|
|
11
|
+
import"../index-yb239ghk.js";
|
|
12
|
+
import"../index-gq9xagx2.js";
|
|
13
|
+
import"../index-8705zxpr.js";
|
|
14
|
+
import"../index-xqxeypt5.js";
|
|
8
15
|
import {
|
|
9
16
|
HARNESS_CLIENT_PROTOCOL_VERSION,
|
|
10
17
|
harnessClientCommandSchema,
|
|
11
18
|
harnessClientRequestSchema
|
|
12
|
-
} from "../index-
|
|
19
|
+
} from "../index-qfpz5y2v.js";
|
|
13
20
|
import"../index-1bx83sw4.js";
|
|
14
21
|
export {
|
|
15
22
|
HARNESS_CLIENT_PROTOCOL_VERSION,
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Host-only integration helpers; never import this entrypoint from a renderer. */
|
|
2
|
+
export { loadModelCatalog } from "../models/catalog-store.js";
|
|
3
|
+
export type { CatalogLoadOptions, CatalogSnapshot } from "../models/catalog-store.js";
|
|
4
|
+
export { vertexConfigured, vertexEnvironment, vertexTokenSource, vertexRoute } from "../providers/vertex-auth.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadModelCatalog
|
|
3
|
+
} from "../index-wg1sd3at.js";
|
|
4
|
+
import {
|
|
5
|
+
vertexConfigured,
|
|
6
|
+
vertexEnvironment,
|
|
7
|
+
vertexRoute,
|
|
8
|
+
vertexTokenSource
|
|
9
|
+
} from "../index-8705zxpr.js";
|
|
10
|
+
import"../index-xqxeypt5.js";
|
|
11
|
+
import"../index-1bx83sw4.js";
|
|
12
|
+
export {
|
|
13
|
+
loadModelCatalog,
|
|
14
|
+
vertexConfigured,
|
|
15
|
+
vertexEnvironment,
|
|
16
|
+
vertexRoute,
|
|
17
|
+
vertexTokenSource
|
|
18
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Versioned, host-only Desktop update/recovery contract. No renderer or SQL API. */
|
|
2
|
+
export { DESKTOP_DATABASE_BACKUP_LIMIT as HOST_DATABASE_SNAPSHOT_LIMIT, createDesktopDatabaseBackup as createHostDatabaseSnapshot, verifyDesktopDatabaseBackup as verifyHostDatabaseSnapshot, verifyDesktopDatabaseState as verifyHostDatabaseState } from "../persistence/host-database-backup.js";
|
|
3
|
+
export type { DesktopDatabaseBackup as HostDatabaseSnapshot, DesktopBackupConfig as HostDatabaseSnapshotConfig } from "../persistence/host-database-backup.js";
|
|
4
|
+
export { HARNESS_SQLITE_FILE } from "../persistence/operations.js";
|
|
5
|
+
export { validateStateDirectory } from "../persistence/state-directory.js";
|
|
6
|
+
export { validateRecordedWorkspace } from "../persistence/recorded-workspace.js";
|
|
7
|
+
export { acquireSqliteAccess, adoptExclusiveSqliteAccess, exclusiveSqliteAccessDescriptor } from "../persistence/sqlite-access.js";
|
|
8
|
+
export type { SqliteAccessLease } from "../persistence/sqlite-access.js";
|
|
9
|
+
export { protectPersistenceSecret } from "../persistence/persistence-secrets.js";
|
|
10
|
+
export { readRegularFileNoFollow, statRegularFileNoFollow } from "../workspace/file-security.js";
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createArchivedHarnessStateBackup,
|
|
3
|
+
createHarnessStateBackup,
|
|
4
|
+
validateRecordedWorkspace
|
|
5
|
+
} from "../index-a97bezbz.js";
|
|
6
|
+
import {
|
|
7
|
+
HARNESS_SQLITE_FILE
|
|
8
|
+
} from "../index-h0mqx2ed.js";
|
|
9
|
+
import {
|
|
10
|
+
validateStateDirectory
|
|
11
|
+
} from "../index-42gp2hy8.js";
|
|
12
|
+
import"../index-z0qxmkm2.js";
|
|
13
|
+
import {
|
|
14
|
+
SqliteDatabase,
|
|
15
|
+
acquireSqliteAccess,
|
|
16
|
+
adoptExclusiveSqliteAccess,
|
|
17
|
+
exclusiveSqliteAccessDescriptor,
|
|
18
|
+
protectPersistenceSecret,
|
|
19
|
+
readRegularFileNoFollow,
|
|
20
|
+
statRegularFileNoFollow
|
|
21
|
+
} from "../index-yb239ghk.js";
|
|
22
|
+
import"../index-gq9xagx2.js";
|
|
23
|
+
import"../index-8705zxpr.js";
|
|
24
|
+
import"../index-xqxeypt5.js";
|
|
25
|
+
import"../index-1bx83sw4.js";
|
|
26
|
+
|
|
27
|
+
// src/persistence/host-database-backup.ts
|
|
28
|
+
import { createHash } from "node:crypto";
|
|
29
|
+
import { constants } from "node:fs";
|
|
30
|
+
import { chmod, lstat, mkdir, mkdtemp, open, realpath, rm } from "node:fs/promises";
|
|
31
|
+
import path from "node:path";
|
|
32
|
+
var DESKTOP_DATABASE_BACKUP_LIMIT = 128 * 1024 * 1024;
|
|
33
|
+
function assertIdle(db) {
|
|
34
|
+
const terminal = "('completed','failed','cancelled','timed_out')";
|
|
35
|
+
if (db.query(`SELECT count(*) AS n FROM zhivex_agent_runs WHERE json_extract(state_json,'$.status') IS NULL OR json_extract(state_json,'$.status') NOT IN ${terminal} OR coalesce(json_array_length(json_extract(state_json,'$.pendingApprovals')),0)>0`).get().n || db.query("SELECT count(*) AS n FROM zhivex_agent_runs_leases WHERE expires_at_ms > ?").get(Date.now()).n || db.query(`SELECT count(*) AS n FROM zhivex_cli_session_runs WHERE status NOT IN ${terminal}`).get().n || db.query("SELECT count(*) AS n FROM zhivex_agent_runs_tool_journal WHERE json_extract(entry_json,'$.status') IS NULL OR json_extract(entry_json,'$.status') NOT IN ('completed','failed')").get().n)
|
|
36
|
+
throw new Error("BACKUP_WORK_ACTIVE");
|
|
37
|
+
}
|
|
38
|
+
async function privateDirectory(directory) {
|
|
39
|
+
const stat = await lstat(directory);
|
|
40
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || stat.uid !== process.getuid?.() || stat.mode & 63)
|
|
41
|
+
throw new Error;
|
|
42
|
+
return realpath(directory);
|
|
43
|
+
}
|
|
44
|
+
async function createDesktopDatabaseBackup(config, backupRoot) {
|
|
45
|
+
let directory;
|
|
46
|
+
try {
|
|
47
|
+
await validateStateDirectory(config.workspace, config.stateDirectory);
|
|
48
|
+
const sourceDirectory = await privateDirectory(config.stateDirectory);
|
|
49
|
+
const source = path.join(sourceDirectory, HARNESS_SQLITE_FILE);
|
|
50
|
+
const sourceStat = await statRegularFileNoFollow(source, { label: "desktop database", requireSingleLink: true });
|
|
51
|
+
if (sourceStat.uid !== process.getuid?.() || sourceStat.mode & 63 || sourceStat.size > DESKTOP_DATABASE_BACKUP_LIMIT)
|
|
52
|
+
throw new Error;
|
|
53
|
+
await mkdir(backupRoot, { recursive: true, mode: 448 });
|
|
54
|
+
directory = await mkdtemp(path.join(await privateDirectory(backupRoot), "database-"));
|
|
55
|
+
const destination = path.join(directory, HARNESS_SQLITE_FILE);
|
|
56
|
+
const sourceDb = new SqliteDatabase(source, { readonly: true, ...config.accessLease ? { accessLease: config.accessLease } : {} });
|
|
57
|
+
try {
|
|
58
|
+
sourceDb.exec("PRAGMA busy_timeout=1000");
|
|
59
|
+
const before = sourceDb.query("PRAGMA data_version").get().data_version;
|
|
60
|
+
if (sourceDb.query("PRAGMA page_count").get().page_count * sourceDb.query("PRAGMA page_size").get().page_size > DESKTOP_DATABASE_BACKUP_LIMIT)
|
|
61
|
+
throw new Error;
|
|
62
|
+
assertIdle(sourceDb);
|
|
63
|
+
sourceDb.query("VACUUM INTO ?").run(destination);
|
|
64
|
+
if (sourceDb.query("PRAGMA data_version").get().data_version !== before)
|
|
65
|
+
throw new Error;
|
|
66
|
+
} finally {
|
|
67
|
+
sourceDb.close();
|
|
68
|
+
}
|
|
69
|
+
await chmod(destination, 384);
|
|
70
|
+
const snapshot = new SqliteDatabase(destination, { readonly: true });
|
|
71
|
+
try {
|
|
72
|
+
if (snapshot.query("PRAGMA quick_check").all().some((row) => row.quick_check !== "ok"))
|
|
73
|
+
throw new Error;
|
|
74
|
+
assertIdle(snapshot);
|
|
75
|
+
} finally {
|
|
76
|
+
snapshot.close();
|
|
77
|
+
}
|
|
78
|
+
const logical = await (config.workspaceAbsent ? createArchivedHarnessStateBackup : createHarnessStateBackup)({ ...config, stateDirectory: directory, storeBackend: "sqlite" });
|
|
79
|
+
const finalDb = new SqliteDatabase(destination);
|
|
80
|
+
try {
|
|
81
|
+
const checkpoint = finalDb.query("PRAGMA wal_checkpoint(TRUNCATE)").get();
|
|
82
|
+
if (checkpoint && checkpoint.busy !== 0)
|
|
83
|
+
throw new Error;
|
|
84
|
+
} finally {
|
|
85
|
+
finalDb.close();
|
|
86
|
+
}
|
|
87
|
+
const file = await open(destination, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
88
|
+
try {
|
|
89
|
+
await file.sync();
|
|
90
|
+
} finally {
|
|
91
|
+
await file.close();
|
|
92
|
+
}
|
|
93
|
+
const contents = await readRegularFileNoFollow(destination, { label: "desktop backup", maxBytes: DESKTOP_DATABASE_BACKUP_LIMIT, requireSingleLink: true });
|
|
94
|
+
const result = { directory, size: contents.contents.length, sha256: createHash("sha256").update(contents.contents).digest("hex"), logicalChecksum: logical.checksum };
|
|
95
|
+
const parent = await open(directory, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
96
|
+
try {
|
|
97
|
+
await parent.sync();
|
|
98
|
+
} finally {
|
|
99
|
+
await parent.close();
|
|
100
|
+
}
|
|
101
|
+
const rootHandle = await open(path.dirname(directory), constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
102
|
+
try {
|
|
103
|
+
await rootHandle.sync();
|
|
104
|
+
} finally {
|
|
105
|
+
await rootHandle.close();
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
} catch {
|
|
109
|
+
if (directory)
|
|
110
|
+
try {
|
|
111
|
+
await rm(directory, { recursive: true, force: true });
|
|
112
|
+
} catch {
|
|
113
|
+
throw new Error("DESKTOP_BACKUP_CLEANUP_FAILED");
|
|
114
|
+
}
|
|
115
|
+
throw new Error("DESKTOP_BACKUP_FAILED");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function verifyDesktopDatabaseBackup(backup) {
|
|
119
|
+
try {
|
|
120
|
+
await privateDirectory(backup.directory);
|
|
121
|
+
const contents = await readRegularFileNoFollow(path.join(backup.directory, HARNESS_SQLITE_FILE), { label: "desktop backup", maxBytes: DESKTOP_DATABASE_BACKUP_LIMIT, requireSingleLink: true });
|
|
122
|
+
if (contents.stat.uid !== process.getuid?.() || contents.stat.mode & 63 || contents.contents.length !== backup.size || createHash("sha256").update(contents.contents).digest("hex") !== backup.sha256)
|
|
123
|
+
throw new Error;
|
|
124
|
+
} catch {
|
|
125
|
+
throw new Error("DESKTOP_BACKUP_INVALID");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function verifyDesktopDatabaseState(config) {
|
|
129
|
+
try {
|
|
130
|
+
await validateRecordedWorkspace(config.workspace, config.workspaceAbsent === true);
|
|
131
|
+
await validateStateDirectory(config.workspace, config.stateDirectory);
|
|
132
|
+
const directory = await privateDirectory(config.stateDirectory), filename = path.join(directory, HARNESS_SQLITE_FILE);
|
|
133
|
+
const stat = await statRegularFileNoFollow(filename, { label: "desktop state", requireSingleLink: true });
|
|
134
|
+
if (stat.uid !== process.getuid?.() || stat.mode & 63 || stat.size > DESKTOP_DATABASE_BACKUP_LIMIT)
|
|
135
|
+
throw new Error;
|
|
136
|
+
exclusiveSqliteAccessDescriptor(config.accessLease, filename);
|
|
137
|
+
const db = new SqliteDatabase(filename, { readonly: true, create: false, accessLease: config.accessLease });
|
|
138
|
+
try {
|
|
139
|
+
db.exec("PRAGMA busy_timeout=1000");
|
|
140
|
+
if (db.query("PRAGMA page_count").get().page_count * db.query("PRAGMA page_size").get().page_size > DESKTOP_DATABASE_BACKUP_LIMIT)
|
|
141
|
+
throw new Error;
|
|
142
|
+
if (db.query("PRAGMA quick_check").all().some((row) => row.quick_check !== "ok"))
|
|
143
|
+
throw new Error;
|
|
144
|
+
assertIdle(db);
|
|
145
|
+
} finally {
|
|
146
|
+
db.close();
|
|
147
|
+
}
|
|
148
|
+
await createArchivedHarnessStateBackup(config, { accessLease: config.accessLease });
|
|
149
|
+
exclusiveSqliteAccessDescriptor(config.accessLease, filename);
|
|
150
|
+
} catch {
|
|
151
|
+
throw new Error("DESKTOP_DATABASE_STATE_INVALID");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
export {
|
|
155
|
+
HARNESS_SQLITE_FILE,
|
|
156
|
+
DESKTOP_DATABASE_BACKUP_LIMIT as HOST_DATABASE_SNAPSHOT_LIMIT,
|
|
157
|
+
acquireSqliteAccess,
|
|
158
|
+
adoptExclusiveSqliteAccess,
|
|
159
|
+
createDesktopDatabaseBackup as createHostDatabaseSnapshot,
|
|
160
|
+
exclusiveSqliteAccessDescriptor,
|
|
161
|
+
protectPersistenceSecret,
|
|
162
|
+
readRegularFileNoFollow,
|
|
163
|
+
statRegularFileNoFollow,
|
|
164
|
+
validateRecordedWorkspace,
|
|
165
|
+
validateStateDirectory,
|
|
166
|
+
verifyDesktopDatabaseBackup as verifyHostDatabaseSnapshot,
|
|
167
|
+
verifyDesktopDatabaseState as verifyHostDatabaseState
|
|
168
|
+
};
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -59,3 +59,15 @@ export { serveAcpStdio } from "../client/acp-stdio.js";
|
|
|
59
59
|
export { openWorkspaceCheckpointStore } from "../persistence/workspace-checkpoints.js";
|
|
60
60
|
export type { WorkspaceCheckpoint, WorkspaceRestoreOperation } from "../persistence/workspace-checkpoints.js";
|
|
61
61
|
export type { HarnessMcpResourceClient } from "../integrations/mcp.js";
|
|
62
|
+
/** Experimental read-only host policy view. */
|
|
63
|
+
export { inspectHarnessPolicy } from "../runtime/policy-inspection.js";
|
|
64
|
+
export type { HarnessPolicyInspection } from "../runtime/policy-inspection.js";
|
|
65
|
+
/** Experimental application-owned task acceptance; enforcement is a separate layer. */
|
|
66
|
+
export { taskAcceptanceContractSchema, compileTaskAcceptanceContract } from '../runtime/task-acceptance.js';
|
|
67
|
+
export type { TaskAcceptanceContract } from '../runtime/task-acceptance.js';
|
|
68
|
+
export { inspectHarnessTaskAcceptance, reviseHarnessTaskAcceptance } from '../runtime/task-acceptance-host.js';
|
|
69
|
+
export type { TaskAcceptanceLedger } from '../runtime/task-acceptance-record.js';
|
|
70
|
+
/** Experimental bounded portable governance projection. */
|
|
71
|
+
export { exportHarnessGovernanceReport, harnessGovernanceReportSchema, renderHarnessGovernanceMarkdown } from '../persistence/governance-report.js';
|
|
72
|
+
export type { HarnessGovernanceReport, HarnessGovernanceOptions } from '../persistence/governance-report.js';
|
|
73
|
+
export type { GovernanceSessionSource } from '../persistence/governance-context.js';
|