@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
|
@@ -123,12 +123,36 @@ var scoped = { projectId: id };
|
|
|
123
123
|
var session = { ...scoped, sessionId: id };
|
|
124
124
|
var run = { ...session, runId: id };
|
|
125
125
|
var mutation = { idempotencyKey: id };
|
|
126
|
+
var checkpoint = { ...session, checkpointId: z.string().uuid() };
|
|
127
|
+
var restore = { ...session, operationId: z.string().uuid() };
|
|
128
|
+
var fileDigest = z.string().regex(/^sha256:[a-f0-9]{64}$/);
|
|
126
129
|
var harnessClientCommandSchema = z.discriminatedUnion("method", [
|
|
127
130
|
z.object({ method: z.literal("project.get"), ...scoped }).strict(),
|
|
131
|
+
z.object({ method: z.literal("policy.get"), ...scoped }).strict(),
|
|
128
132
|
z.object({ method: z.literal("session.list"), ...scoped, search: z.string().max(256).optional() }).strict(),
|
|
129
133
|
z.object({ method: z.literal("session.create"), ...scoped, ...mutation, title: z.string().max(256).optional() }).strict(),
|
|
130
134
|
z.object({ method: z.literal("session.get"), ...session }).strict(),
|
|
131
135
|
z.object({ method: z.literal("session.rename"), ...session, ...mutation, expectedRevision: revision, title: z.string().max(256) }).strict(),
|
|
136
|
+
z.object({ method: z.literal("checkpoint.list"), ...session }).strict(),
|
|
137
|
+
z.object({ method: z.literal("checkpoint.inspect"), ...checkpoint }).strict(),
|
|
138
|
+
z.object({
|
|
139
|
+
method: z.literal("checkpoint.capture"),
|
|
140
|
+
...session,
|
|
141
|
+
...mutation,
|
|
142
|
+
expectedRevision: revision,
|
|
143
|
+
turnId: id,
|
|
144
|
+
paths: z.array(z.string().min(1).max(1024)).min(1).max(20)
|
|
145
|
+
}).strict(),
|
|
146
|
+
z.object({
|
|
147
|
+
method: z.literal("restore.prepare"),
|
|
148
|
+
...checkpoint,
|
|
149
|
+
...mutation,
|
|
150
|
+
expectedRevision: revision,
|
|
151
|
+
expected: z.record(z.string().min(1).max(1024), fileDigest).refine((value) => Object.keys(value).length >= 1 && Object.keys(value).length <= 20)
|
|
152
|
+
}).strict(),
|
|
153
|
+
z.object({ method: z.literal("restore.get"), ...restore }).strict(),
|
|
154
|
+
z.object({ method: z.literal("restore.apply"), ...restore, ...mutation, expectedRevision: revision, reviewedProposalId: fileDigest }).strict(),
|
|
155
|
+
z.object({ method: z.literal("restore.recoverFork"), ...restore, ...mutation, expectedRevision: revision, forkSessionId: id }).strict(),
|
|
132
156
|
z.object({ method: z.literal("run.start"), ...session, ...mutation, expectedRevision: revision, prompt: z.string().min(1).max(64 * 1024) }).strict(),
|
|
133
157
|
z.object({ method: z.literal("run.get"), ...run, includeReview: z.boolean().optional(), includeDiff: z.boolean().optional(), decisionOffset: z.number().int().min(0).max(512).optional() }).strict(),
|
|
134
158
|
z.object({
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createHarnessClientAdapter
|
|
3
|
-
|
|
2
|
+
createHarnessClientAdapter,
|
|
3
|
+
harnessPolicyDecisionEventSchema
|
|
4
|
+
} from "./index-hnaxfpb5.js";
|
|
4
5
|
import {
|
|
5
|
-
SqliteDatabase,
|
|
6
6
|
openCliSessionStore
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-42gp2hy8.js";
|
|
8
|
+
import {
|
|
9
|
+
SqliteDatabase
|
|
10
|
+
} from "./index-yb239ghk.js";
|
|
8
11
|
import {
|
|
9
12
|
harnessClientRequestSchema,
|
|
10
13
|
streamEventDocument
|
|
11
|
-
} from "./index-
|
|
14
|
+
} from "./index-qfpz5y2v.js";
|
|
12
15
|
import {
|
|
13
16
|
__require
|
|
14
17
|
} from "./index-1bx83sw4.js";
|
|
@@ -139,6 +142,9 @@ var openHarnessActivityStore = async (config, options = {}) => {
|
|
|
139
142
|
}
|
|
140
143
|
};
|
|
141
144
|
return {
|
|
145
|
+
policyDecision(sessionId, runId, event) {
|
|
146
|
+
write(sessionId, runId, redactValue(harnessPolicyDecisionEventSchema.parse(event)));
|
|
147
|
+
},
|
|
142
148
|
prompt(sessionId, runId, prompt) {
|
|
143
149
|
const safe = redact(prompt);
|
|
144
150
|
const truncated = Buffer.byteLength(safe) > 60 * 1024;
|
|
@@ -193,10 +199,10 @@ var openHarnessActivityStore = async (config, options = {}) => {
|
|
|
193
199
|
if (after > current.state.sequence)
|
|
194
200
|
throw new Error("ACTIVITY_CURSOR_AHEAD");
|
|
195
201
|
if (expired)
|
|
196
|
-
return { schemaVersion: 1, cursorExpired: true, nextCursor: current.state.sequence, events: [], snapshot: current.state };
|
|
197
|
-
const rows = db.query("SELECT sequence,session,run,at,activity FROM client_activity_events WHERE scope=? AND session=? AND sequence>? ORDER BY sequence LIMIT
|
|
198
|
-
const events = rows.map((row) => ({ schemaVersion: 1, eventId: createHash("sha256").update(`${scope}:${row.sequence}`).digest("hex"), sequence: row.sequence, sessionId: row.session, runId: row.run, at: row.at, activity: JSON.parse(row.activity) }));
|
|
199
|
-
return { schemaVersion: 1, cursorExpired: false, nextCursor: events.at(-1)?.sequence ?? after, events };
|
|
202
|
+
return { schemaVersion: 1, cursorExpired: true, hasMore: false, policyEvidenceIncomplete: true, nextCursor: current.state.sequence, events: [], snapshot: current.state };
|
|
203
|
+
const rows = db.query("SELECT sequence,session,run,at,activity FROM client_activity_events WHERE scope=? AND session=? AND sequence>? ORDER BY sequence LIMIT 201").all(scope, sessionId, after);
|
|
204
|
+
const events = rows.slice(0, 200).map((row) => ({ schemaVersion: 1, eventId: createHash("sha256").update(`${scope}:${row.sequence}`).digest("hex"), sequence: row.sequence, sessionId: row.session, runId: row.run, at: row.at, activity: JSON.parse(row.activity) }));
|
|
205
|
+
return { schemaVersion: 1, cursorExpired: false, hasMore: rows.length > 200, policyEvidenceIncomplete: current.expiredThrough > 0, nextCursor: events.at(-1)?.sequence ?? after, events };
|
|
200
206
|
},
|
|
201
207
|
close() {
|
|
202
208
|
db.close();
|
|
@@ -312,7 +318,7 @@ var startHarnessLocalService = async (harness, options) => {
|
|
|
312
318
|
}
|
|
313
319
|
const parsed = harnessClientRequestSchema.safeParse(value);
|
|
314
320
|
const method = parsed.success ? parsed.data.command.method : undefined;
|
|
315
|
-
const mutation = method !== undefined && !["project.get", "session.list", "session.get", "run.get"].includes(method);
|
|
321
|
+
const mutation = method !== undefined && !["project.get", "policy.get", "session.list", "session.get", "run.get", "checkpoint.list", "checkpoint.inspect", "restore.get"].includes(method);
|
|
316
322
|
if (paused && mutation && method !== "run.cancel")
|
|
317
323
|
return send(res, 503, fault("SERVICE_PAUSED"));
|
|
318
324
|
if (mutation)
|
|
@@ -359,6 +365,7 @@ var startHarnessLocalService = async (harness, options) => {
|
|
|
359
365
|
});
|
|
360
366
|
adapter = await createHarnessClientAdapter(harness, {
|
|
361
367
|
...options.approvalNow ? { now: options.approvalNow } : {},
|
|
368
|
+
onPolicyDecision: (sessionId, runId, event) => activity.policyDecision(sessionId, runId, event),
|
|
362
369
|
onPrompt: (sessionId, runId, prompt) => activity.prompt(sessionId, runId, prompt),
|
|
363
370
|
onEvent: (sessionId, runId, event) => activity.append(sessionId, runId, event),
|
|
364
371
|
onCheckpoint: (sessionId, runId, status) => activity.checkpoint(sessionId, runId, status)
|
|
@@ -377,6 +384,22 @@ var startHarnessLocalService = async (harness, options) => {
|
|
|
377
384
|
return {
|
|
378
385
|
socketPath,
|
|
379
386
|
credentialsPath,
|
|
387
|
+
async dispatchReviewed(request) {
|
|
388
|
+
if (closing || paused)
|
|
389
|
+
throw new Error("SERVICE_ADMISSION_PAUSED");
|
|
390
|
+
if (!adapter?.dispatchReviewed)
|
|
391
|
+
throw new Error("EXPLICIT_REVIEW_UNAVAILABLE");
|
|
392
|
+
activeMutations++;
|
|
393
|
+
const operation = adapter.dispatchReviewed(request);
|
|
394
|
+
const tracked = operation.then(() => {}, () => {});
|
|
395
|
+
pending.add(tracked);
|
|
396
|
+
try {
|
|
397
|
+
return await operation;
|
|
398
|
+
} finally {
|
|
399
|
+
pending.delete(tracked);
|
|
400
|
+
activeMutations--;
|
|
401
|
+
}
|
|
402
|
+
},
|
|
380
403
|
pauseAdmission() {
|
|
381
404
|
if (closing)
|
|
382
405
|
throw new Error("SERVICE_CLOSING");
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bundledModelCatalog,
|
|
3
|
+
modelCatalogSchema
|
|
4
|
+
} from "./index-xqxeypt5.js";
|
|
5
|
+
|
|
6
|
+
// src/models/catalog-store.ts
|
|
7
|
+
import { mkdir, readFile, rename, writeFile, unlink } from "node:fs/promises";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
11
|
+
var MAX_BYTES = 1024 * 1024;
|
|
12
|
+
var TTL = 60 * 60 * 1000;
|
|
13
|
+
async function loadModelCatalog(options = {}) {
|
|
14
|
+
const fallback = { catalog: bundledModelCatalog, source: "bundled", stale: false };
|
|
15
|
+
const source = options.url ?? process.env.ZHIVEX_MODEL_CATALOG_URL;
|
|
16
|
+
if (!source)
|
|
17
|
+
return fallback;
|
|
18
|
+
let url;
|
|
19
|
+
try {
|
|
20
|
+
url = new URL(source);
|
|
21
|
+
if (url.protocol !== "https:" || url.username || url.password || url.hash)
|
|
22
|
+
return { ...fallback, stale: true };
|
|
23
|
+
} catch {
|
|
24
|
+
return { ...fallback, stale: true };
|
|
25
|
+
}
|
|
26
|
+
const now = options.now ?? Date.now();
|
|
27
|
+
const directory = options.cacheDirectory ?? path.join(homedir(), ".zhivex", "model-catalog");
|
|
28
|
+
const file = path.join(directory, `${createHash("sha256").update(url.href).digest("hex")}.json`);
|
|
29
|
+
let cached;
|
|
30
|
+
try {
|
|
31
|
+
const raw = await readFile(file, "utf8");
|
|
32
|
+
if (Buffer.byteLength(raw) > MAX_BYTES + 1024)
|
|
33
|
+
throw new Error("CATALOG_TOO_LARGE");
|
|
34
|
+
const envelope = JSON.parse(raw);
|
|
35
|
+
if (envelope.url !== url.href || !Number.isFinite(envelope.fetchedAt))
|
|
36
|
+
throw new Error("INVALID_CACHE");
|
|
37
|
+
cached = { catalog: modelCatalogSchema.parse(envelope.catalog), source: "cache", stale: true };
|
|
38
|
+
if (!options.refresh && now >= envelope.fetchedAt && now - envelope.fetchedAt < TTL)
|
|
39
|
+
return { ...cached, stale: false };
|
|
40
|
+
} catch {}
|
|
41
|
+
try {
|
|
42
|
+
const response = await (options.fetcher ?? fetch)(url, {
|
|
43
|
+
signal: AbortSignal.timeout(3000),
|
|
44
|
+
redirect: "error",
|
|
45
|
+
headers: { accept: "application/json" }
|
|
46
|
+
});
|
|
47
|
+
if (!response.ok || !response.body)
|
|
48
|
+
throw new Error("CATALOG_FETCH_FAILED");
|
|
49
|
+
const reader = response.body.getReader();
|
|
50
|
+
const chunks = [];
|
|
51
|
+
let size = 0;
|
|
52
|
+
try {
|
|
53
|
+
for (;; ) {
|
|
54
|
+
const { value, done } = await reader.read();
|
|
55
|
+
if (done)
|
|
56
|
+
break;
|
|
57
|
+
size += value.byteLength;
|
|
58
|
+
if (size > MAX_BYTES)
|
|
59
|
+
throw new Error("CATALOG_TOO_LARGE");
|
|
60
|
+
chunks.push(value);
|
|
61
|
+
}
|
|
62
|
+
} finally {
|
|
63
|
+
await reader.cancel().catch(() => {});
|
|
64
|
+
}
|
|
65
|
+
const catalog = modelCatalogSchema.parse(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
66
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
67
|
+
try {
|
|
68
|
+
await mkdir(directory, { recursive: true, mode: 448 });
|
|
69
|
+
await writeFile(temporary, JSON.stringify({ url: url.href, fetchedAt: now, catalog }), { mode: 384, flag: "wx" });
|
|
70
|
+
await rename(temporary, file);
|
|
71
|
+
} catch {} finally {
|
|
72
|
+
await unlink(temporary).catch(() => {});
|
|
73
|
+
}
|
|
74
|
+
return { catalog, source: "remote", stale: false };
|
|
75
|
+
} catch {
|
|
76
|
+
return cached ?? { ...fallback, stale: true };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { loadModelCatalog };
|