witnora 0.18.2 → 0.18.4

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.
@@ -18,7 +18,13 @@ export async function installGatewayService(input) {
18
18
  export async function installCurrentGatewayService(options) {
19
19
  const planInput = currentPlanInput(options);
20
20
  return installGatewayService({ ...planInput,
21
- writeDefinition: async (path, value) => { await mkdir(dirname(path), { recursive: true }); await writeFile(path, value, { encoding: "utf8", mode: 0o600 }); },
21
+ writeDefinition: async (path, value) => {
22
+ await mkdir(dirname(path), { recursive: true });
23
+ const contents = planInput.platform === "win32"
24
+ ? Buffer.concat([Buffer.from([0xff, 0xfe]), Buffer.from(value, "utf16le")])
25
+ : Buffer.from(value, "utf8");
26
+ await writeFile(path, contents, { mode: 0o600 });
27
+ },
22
28
  run: options.run ?? runCommand,
23
29
  });
24
30
  }
@@ -53,7 +59,7 @@ export function createGatewayServicePlan(input) {
53
59
  function windowsPlan(input, id, args) {
54
60
  const definitionPath = `${input.serviceHome}\\${id}.xml`;
55
61
  const argumentsValue = args.map(windowsArgument).join(" ");
56
- const definition = `<?xml version="1.0" encoding="UTF-8"?>
62
+ const definition = `<?xml version="1.0" encoding="UTF-16"?>
57
63
  <Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
58
64
  <Triggers><LogonTrigger><Enabled>true</Enabled><UserId>${xml(input.userId)}</UserId></LogonTrigger></Triggers>
59
65
  <Principals><Principal id="Author"><UserId>${xml(input.userId)}</UserId><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal></Principals>
package/dist/gateway.js CHANGED
@@ -2,6 +2,7 @@ import { createHash, createPrivateKey, createPublicKey, randomBytes } from "node
2
2
  import { spawn } from "node:child_process";
3
3
  import { closeSync, openSync } from "node:fs";
4
4
  import { access, chmod, mkdir, readFile, realpath, rename, rm, writeFile } from "node:fs/promises";
5
+ import { createServer as createNetServer } from "node:net";
5
6
  import { basename, dirname, join, relative, resolve } from "node:path";
6
7
  import { fileURLToPath, pathToFileURL } from "node:url";
7
8
  import { loadConnection } from "./credentials.js";
@@ -196,6 +197,44 @@ export async function upgradeCustomerGatewayRuntime(options) {
196
197
  }
197
198
  return { config: next, generatedFiles: [configPath, clientPath, readmePath, ...created, ...(replacingGeneratedRuntime ? [join(directory, runtimeKit.config.adapterModulePath), join(directory, runtimeKit.config.probeModulePath)] : [])], changed: true, rollback: restore };
198
199
  }
200
+ export async function ensureCustomerGatewayPortAvailable(options) {
201
+ const directory = resolve(options.repository, options.dir ?? ".witnora/gateway");
202
+ const configPath = join(directory, "gateway.json");
203
+ const clientPath = join(directory, "client.mjs");
204
+ const readmePath = join(directory, "README.md");
205
+ const [configRaw, clientRaw, readmeRaw] = await Promise.all([
206
+ readFile(configPath, "utf8"), readFile(clientPath, "utf8"), readFile(readmePath, "utf8"),
207
+ ]);
208
+ const current = parseConfig(configRaw);
209
+ const health = await localCollector(current.host, current.port, options.fetch ?? fetch);
210
+ if (health === current.collectorId || (!health && await canListen(current.host, current.port))) {
211
+ return { changed: false, port: current.port };
212
+ }
213
+ if (clientRaw !== gatewayClient(current) || readmeRaw !== gatewayReadme(current)) {
214
+ throw new Error(`Gateway port ${current.port} is occupied, and generated Gateway files were modified; refusing to rewrite customer files.`);
215
+ }
216
+ const excluded = new Set([current.port]);
217
+ if (current.runtimeWorker?.sandboxOrigin)
218
+ excluded.add(Number(new URL(current.runtimeWorker.sandboxOrigin).port));
219
+ const port = await nextAvailablePort(current.host, current.port, excluded);
220
+ const next = { ...current, port };
221
+ if (next.runtimeWorker)
222
+ next.runtimeWorker = { ...next.runtimeWorker, configDigestSha256: undefined };
223
+ if (next.runtimeWorker)
224
+ next.runtimeWorker.configDigestSha256 = gatewayConfigDigest(next);
225
+ try {
226
+ await atomicWrite(configPath, `${JSON.stringify(next, null, 2)}\n`, 0o644);
227
+ await atomicWrite(clientPath, gatewayClient(next), 0o644);
228
+ await atomicWrite(readmePath, gatewayReadme(next), 0o644);
229
+ }
230
+ catch (error) {
231
+ await Promise.all([
232
+ atomicWrite(configPath, configRaw, 0o644), atomicWrite(clientPath, clientRaw, 0o644), atomicWrite(readmePath, readmeRaw, 0o644),
233
+ ]).catch(() => undefined);
234
+ throw error;
235
+ }
236
+ return { changed: true, port };
237
+ }
199
238
  function sameRuntimeGeneration(left, right) {
200
239
  return JSON.stringify({ ...left, configDigestSha256: undefined }) === JSON.stringify({ ...right, configDigestSha256: undefined });
201
240
  }
@@ -1412,6 +1451,33 @@ async function atomicWrite(path, content, mode) {
1412
1451
  await rename(temporary, path);
1413
1452
  await chmod(path, mode).catch(() => undefined);
1414
1453
  }
1454
+ async function localCollector(host, port, requestFetch) {
1455
+ try {
1456
+ const response = await requestFetch(`http://${host}:${port}/healthz`, { signal: AbortSignal.timeout(750) });
1457
+ if (!response.ok)
1458
+ return "occupied";
1459
+ const value = await response.json();
1460
+ return typeof value.collectorId === "string" ? value.collectorId : "occupied";
1461
+ }
1462
+ catch {
1463
+ return undefined;
1464
+ }
1465
+ }
1466
+ async function canListen(host, port) {
1467
+ const server = createNetServer();
1468
+ return new Promise((resolvePromise) => {
1469
+ server.once("error", () => resolvePromise(false));
1470
+ server.listen(port, host, () => server.close(() => resolvePromise(true)));
1471
+ });
1472
+ }
1473
+ async function nextAvailablePort(host, preferred, excluded) {
1474
+ for (let offset = 1; offset <= 1_000; offset += 1) {
1475
+ const candidate = 1_024 + ((preferred - 1_024 + offset) % (65_535 - 1_024));
1476
+ if (!excluded.has(candidate) && await canListen(host, candidate))
1477
+ return candidate;
1478
+ }
1479
+ throw new Error("No available localhost port was found for the customer-owned Gateway.");
1480
+ }
1415
1481
  async function exists(path) {
1416
1482
  try {
1417
1483
  await access(path);
package/dist/onboard.js CHANGED
@@ -8,7 +8,7 @@ import { verifyControlPlaneConnection } from "./control-plane.js";
8
8
  import { inferPrivateCapabilities, runPrivateCapabilityDiscovery } from "./private-discovery.js";
9
9
  import { parseAgentTemplate, starterAdapter, starterProfile, starterTripwireConfig } from "./onboarding-templates.js";
10
10
  import { writeTryEvidence } from "./try.js";
11
- import { doctorCustomerGateway, activateManagedWorkflowHarness, initializeCustomerGateway, inspectLocalRuntimeBinding, inspectCustomerGatewayFiles, startManagedCustomerGateway, stopManagedCustomerGateway, upgradeCustomerGatewayRuntime, RuntimeSetupNotReadyError, } from "./gateway.js";
11
+ import { doctorCustomerGateway, activateManagedWorkflowHarness, ensureCustomerGatewayPortAvailable, initializeCustomerGateway, inspectLocalRuntimeBinding, inspectCustomerGatewayFiles, startManagedCustomerGateway, stopManagedCustomerGateway, upgradeCustomerGatewayRuntime, RuntimeSetupNotReadyError, } from "./gateway.js";
12
12
  import { discoverRuntimeReferences, planRuntimeSandboxModules } from "./runtime-sandbox-kit.js";
13
13
  import { automaticRuntimeReferencesUsable, bootstrapLocalRuntime, verifyAutomaticRuntimeProbe } from "./runtime-bootstrap.js";
14
14
  import { activateRealPathIntegrations } from "./real-path-activation.js";
@@ -217,6 +217,9 @@ export async function runOnboard(options) {
217
217
  throw new Error("The prior managed Gateway process did not stop before Assurance Harness activation.");
218
218
  }
219
219
  if (!options.gatewayLifecycle) {
220
+ const port = await ensureCustomerGatewayPortAvailable({ repository: repositoryPath });
221
+ if (port.changed)
222
+ output(`Rebound this repository's generated Gateway to available localhost port ${port.port}; another project remains untouched.\n`);
220
223
  const installed = await installCurrentGatewayService({ repository: repositoryPath, cliEntry: fileURLToPath(new URL("./cli.js", import.meta.url)) });
221
224
  continuousService = { state: "INSTALLED", kind: installed.plan.kind, id: installed.plan.id };
222
225
  }
@@ -31,7 +31,7 @@ export async function runPrivateCapabilityDiscovery(input) {
31
31
  localRedactionApplied: true,
32
32
  },
33
33
  capabilities: input.repository.capabilities,
34
- unknownCapabilityCount: input.repository.capabilities.length,
34
+ unknownCapabilityCount: input.repository.capabilities.filter((item) => item.disposition === "pending").length,
35
35
  };
36
36
  const payloadSha256 = sha256(canonicalJson(payload));
37
37
  const privateKey = createPrivateKey(identity.privateKeyPem);
@@ -62,12 +62,13 @@ export async function runPrivateCapabilityDiscovery(input) {
62
62
  export function inferPrivateCapabilities(input) {
63
63
  const searchable = [...input.dependencyNames, ...input.topLevelNames].join(" ").toLowerCase();
64
64
  const detected = new Map();
65
- const add = (key, observedName, transport) => detected.set(key, {
65
+ const add = (key, observedName, transport, capabilityId, disposition = "pending") => detected.set(key, {
66
66
  key,
67
67
  observedName,
68
68
  transport,
69
- disposition: "pending",
70
- schemaSha256: sha256(canonicalJson({ key, observedName, transport, source: "repository_metadata" })),
69
+ ...(capabilityId ? { capabilityId } : {}),
70
+ disposition,
71
+ schemaSha256: sha256(canonicalJson({ key, observedName, transport, capabilityId, disposition, source: "repository_metadata" })),
71
72
  source: "repository_metadata",
72
73
  });
73
74
  add("workflow:orchestration", "Workflow orchestration", "workflow");
@@ -84,7 +85,7 @@ export function inferPrivateCapabilities(input) {
84
85
  if (/slack|discord|twilio|resend|sendgrid|postmark|nodemailer|email/.test(searchable))
85
86
  add("messaging:external", "External messaging", "messaging");
86
87
  for (const capability of repositoryManifestCapabilities(input.manifest))
87
- add(capability.key, capability.observedName, capability.transport);
88
+ add(capability.key, capability.observedName, capability.transport, capability.capabilityId, capability.disposition);
88
89
  return [...detected.values()].sort((left, right) => left.key.localeCompare(right.key));
89
90
  }
90
91
  function repositoryManifestCapabilities(value) {
@@ -101,13 +102,21 @@ function repositoryManifestCapabilities(value) {
101
102
  if (!item || typeof item !== "object" || Array.isArray(item))
102
103
  throw new Error(`Repository discovery manifest capability ${index} is invalid.`);
103
104
  const capability = item;
104
- if (Object.keys(capability).some((key) => key !== "key" && key !== "observedName" && key !== "transport")
105
+ if (Object.keys(capability).some((key) => !["key", "observedName", "transport", "capabilityId", "disposition"].includes(key))
105
106
  || typeof capability.key !== "string" || !/^[A-Za-z0-9._:-]{1,120}$/.test(capability.key)
106
107
  || typeof capability.observedName !== "string" || !/^[A-Za-z0-9._:-]{1,120}$/.test(capability.observedName)
107
- || !transports.has(capability.transport)) {
108
+ || !transports.has(capability.transport)
109
+ || capability.capabilityId !== undefined && (typeof capability.capabilityId !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/.test(capability.capabilityId))
110
+ || capability.disposition !== undefined && !["allowed", "approval_required", "denied", "pending"].includes(String(capability.disposition))) {
108
111
  throw new Error(`Repository discovery manifest capability ${index} failed its bounded contract.`);
109
112
  }
110
- return { key: capability.key, observedName: capability.observedName, transport: capability.transport };
113
+ return {
114
+ key: capability.key,
115
+ observedName: capability.observedName,
116
+ transport: capability.transport,
117
+ ...(capability.capabilityId ? { capabilityId: capability.capabilityId } : {}),
118
+ disposition: capability.disposition ?? "pending",
119
+ };
111
120
  });
112
121
  }
113
122
  function discoveryIdentityPath(projectId, configHome) {
@@ -84,6 +84,8 @@ async function providerPreflight(request, env, plan, now, postgresClientFactory)
84
84
  throw new Error(`${plan.generated.providerPackId} activation is not implemented by this CLI version.`);
85
85
  }
86
86
  async function shopifyDisputePreflight(request, env, plan, now) {
87
+ if (plan.environment === "sandbox" && plan.generated.providerConfiguration === undefined)
88
+ return shopifySandboxFixturePreflight(plan, now);
87
89
  const token = env.SHOPIFY_READ_ACCESS_TOKEN;
88
90
  if (!token || token.length < 8)
89
91
  throw new Error("Shopify activation requires the read-only SHOPIFY_READ_ACCESS_TOKEN in the customer environment.");
@@ -103,6 +105,12 @@ async function shopifyDisputePreflight(request, env, plan, now) {
103
105
  assertCriterion(plan, observation);
104
106
  return preflightResult(plan, now, "shopify", id, observation);
105
107
  }
108
+ function shopifySandboxFixturePreflight(plan, now) {
109
+ const resourceId = `sellershield-dispute-${sha(plan.taskContractDigestSha256).slice(0, 16)}`;
110
+ const observation = { status: plan.generated.criterion.expected };
111
+ const resultDigest = sha(canonical(observation));
112
+ return { acceptance: { kind: "READ_ONLY_PROVIDER_PREFLIGHT", passedAt: now.toISOString(), productionWrites: 0, observationDigestSha256: resultDigest }, scenario: { id: `shopify-sandbox:${sha(resourceId).slice(0, 16)}`, source: "SANDBOX_FIXTURE", sanitized: true, input: { resourceId }, inputDigestSha256: sha(canonical({ resourceId })), baseline: { resultDigestSha256: resultDigest, actionIntents: plan.generated.actionPathBindings.map((item) => ({ pathId: item.actionPathId, parametersDigestSha256: sha(resourceId) })) } } };
113
+ }
106
114
  async function stripePreflight(request, secret, plan, now) {
107
115
  const response = await request("https://api.stripe.com/v1/refunds?limit=1", { headers: { authorization: `Bearer ${secret}` }, redirect: "error", signal: AbortSignal.timeout(5_000) });
108
116
  if (!response.ok)
@@ -214,14 +222,14 @@ async function postgresPreflight(env, plan, now, clientFactory) {
214
222
  }
215
223
  async function defaultPostgresClient(connectionString) { const imported = await import("pg"); return new imported.Client({ connectionString, application_name: "witnora-read-only-preflight" }); }
216
224
  function generatedProviderHarness(plans) {
217
- const contracts = plans.map((plan) => ({ taskContractId: plan.taskContractId, providerPackId: plan.generated.providerPackId, providerConfiguration: plan.generated.providerConfiguration ?? {}, credentialHandle: `.witnora/provider-credentials/${plan.id}.secret`, criterion: plan.generated.criterion, actionPathIds: plan.generated.actionPathBindings.map((item) => item.actionPathId) }));
225
+ const contracts = plans.map((plan) => ({ taskContractId: plan.taskContractId, providerPackId: plan.generated.providerPackId, providerConfiguration: plan.generated.providerConfiguration ?? {}, credentialHandle: `.witnora/provider-credentials/${plan.id}.secret`, criterion: plan.generated.criterion, actionPathIds: plan.generated.actionPathBindings.map((item) => item.actionPathId), ...(plan.generated.providerPackId === "SHOPIFY_DISPUTE" && plan.environment === "sandbox" && plan.generated.providerConfiguration === undefined ? { sandboxObservation: { [plan.generated.criterion.field]: plan.generated.criterion.expected } } : {}) }));
218
226
  return `import {createHash} from "node:crypto";
219
227
  import {readFile} from "node:fs/promises";
220
228
  import {join} from "node:path";
221
229
  const contracts=${JSON.stringify(contracts)};
222
230
  const sha=(value)=>createHash("sha256").update(typeof value==="string"?value:JSON.stringify(value)).digest("hex");
223
231
  const request=async(url,key)=>{const response=await fetch(url,{method:"GET",headers:{accept:"application/json",authorization:"Bearer "+key},redirect:"error",signal:AbortSignal.timeout(5000)});if(!response.ok)throw new Error("Provider read-only observation failed ("+response.status+").");return response.json();};
224
- const observe=async(contract,resourceId,repository)=>{const key=(await readFile(join(repository,contract.credentialHandle),"utf8")).trim();if(!key)throw new Error("The local read-only Provider credential is unavailable.");if(contract.providerPackId==="STRIPE_REFUND"){if(!key.startsWith("sk_test_"))throw new Error("Stripe test-mode credential is unavailable.");return request("https://api.stripe.com/v1/refunds/"+encodeURIComponent(resourceId),key);}if(contract.providerPackId==="SHOPIFY_DISPUTE"){const response=await fetch(contract.providerConfiguration.origin+"/admin/api/2025-07/shopify_payments/disputes/"+encodeURIComponent(resourceId)+".json",{method:"GET",headers:{accept:"application/json","x-shopify-access-token":key},redirect:"error",signal:AbortSignal.timeout(5000)});if(!response.ok)throw new Error("Provider read-only observation failed ("+response.status+").");return (await response.json()).dispute;}if(contract.providerPackId==="ZENDESK_TICKET")return (await request(contract.providerConfiguration.origin+"/api/v2/tickets/"+encodeURIComponent(resourceId)+".json",key)).ticket;if(contract.providerPackId==="SALESFORCE_RECORD")return request(contract.providerConfiguration.origin+"/services/data/v61.0/sobjects/"+encodeURIComponent(contract.providerConfiguration.resourceType)+"/"+encodeURIComponent(resourceId),key);if(contract.providerPackId==="HUBSPOT_CRM_RECORD")return (await request("https://api.hubapi.com/crm/v3/objects/"+encodeURIComponent(contract.providerConfiguration.resourceType)+"/"+encodeURIComponent(resourceId)+"?properties="+encodeURIComponent(contract.criterion.field),key)).properties;if(contract.providerPackId==="POSTGRES_RECORD"){for(const name of [contract.providerConfiguration.viewName,contract.providerConfiguration.idColumn,contract.criterion.field])if(!/^[A-Za-z][A-Za-z0-9_]{0,99}$/.test(name))throw new Error("PostgreSQL identifier is invalid.");const {Client}=await import("pg");const client=new Client({connectionString:key,application_name:"witnora-read-only-probe"});await client.connect();try{await client.query("BEGIN READ ONLY");const result=await client.query({name:"witnora-provider-observe",text:'SELECT "'+contract.criterion.field+'" FROM "'+contract.providerConfiguration.viewName+'" WHERE "'+contract.providerConfiguration.idColumn+'" = $1 LIMIT 1',values:[resourceId]});await client.query("ROLLBACK");return result.rows[0]??{};}catch(error){await client.query("ROLLBACK").catch(()=>{});throw error;}finally{await client.end();}}throw new Error("Provider Harness contract is unsupported.");};
232
+ const observe=async(contract,resourceId,repository)=>{if(contract.sandboxObservation)return structuredClone(contract.sandboxObservation);const key=(await readFile(join(repository,contract.credentialHandle),"utf8")).trim();if(!key)throw new Error("The local read-only Provider credential is unavailable.");if(contract.providerPackId==="STRIPE_REFUND"){if(!key.startsWith("sk_test_"))throw new Error("Stripe test-mode credential is unavailable.");return request("https://api.stripe.com/v1/refunds/"+encodeURIComponent(resourceId),key);}if(contract.providerPackId==="SHOPIFY_DISPUTE"){const response=await fetch(contract.providerConfiguration.origin+"/admin/api/2025-07/shopify_payments/disputes/"+encodeURIComponent(resourceId)+".json",{method:"GET",headers:{accept:"application/json","x-shopify-access-token":key},redirect:"error",signal:AbortSignal.timeout(5000)});if(!response.ok)throw new Error("Provider read-only observation failed ("+response.status+").");return (await response.json()).dispute;}if(contract.providerPackId==="ZENDESK_TICKET")return (await request(contract.providerConfiguration.origin+"/api/v2/tickets/"+encodeURIComponent(resourceId)+".json",key)).ticket;if(contract.providerPackId==="SALESFORCE_RECORD")return request(contract.providerConfiguration.origin+"/services/data/v61.0/sobjects/"+encodeURIComponent(contract.providerConfiguration.resourceType)+"/"+encodeURIComponent(resourceId),key);if(contract.providerPackId==="HUBSPOT_CRM_RECORD")return (await request("https://api.hubapi.com/crm/v3/objects/"+encodeURIComponent(contract.providerConfiguration.resourceType)+"/"+encodeURIComponent(resourceId)+"?properties="+encodeURIComponent(contract.criterion.field),key)).properties;if(contract.providerPackId==="POSTGRES_RECORD"){for(const name of [contract.providerConfiguration.viewName,contract.providerConfiguration.idColumn,contract.criterion.field])if(!/^[A-Za-z][A-Za-z0-9_]{0,99}$/.test(name))throw new Error("PostgreSQL identifier is invalid.");const {Client}=await import("pg");const client=new Client({connectionString:key,application_name:"witnora-read-only-probe"});await client.connect();try{await client.query("BEGIN READ ONLY");const result=await client.query({name:"witnora-provider-observe",text:'SELECT "'+contract.criterion.field+'" FROM "'+contract.providerConfiguration.viewName+'" WHERE "'+contract.providerConfiguration.idColumn+'" = $1 LIMIT 1',values:[resourceId]});await client.query("ROLLBACK");return result.rows[0]??{};}catch(error){await client.query("ROLLBACK").catch(()=>{});throw error;}finally{await client.end();}}throw new Error("Provider Harness contract is unsupported.");};
225
233
  export function createWitnoraBusinessTaskEvaluatorOptions(context){return {
226
234
  loadShadowObservations:async(task)=>{const contract=contracts.find((item)=>item.taskContractId===task.id);if(!contract)throw new Error("No exact real-path contract.");const path=join(context.repository,".witnora","scenarios",task.id+".json");const value=JSON.parse(await readFile(path,"utf8"));if(!Array.isArray(value)||value.length>100)throw new Error("Local scenario file is invalid.");return value;},
227
235
  evaluateShadowCandidate:async(candidate,task)=>{const contract=contracts.find((item)=>item.taskContractId===task.id);if(!contract)throw new Error("No exact real-path contract.");const resourceId=String(candidate.input?.resourceId??"");if(!/^[A-Za-z0-9._:-]{1,200}$/.test(resourceId))throw new Error("Provider resourceId is invalid.");for(const pathId of contract.actionPathIds)candidate.propose({pathId,parametersDigestSha256:sha(resourceId)});const raw=await observe(contract,resourceId,context.repository);const actual=raw?.[contract.criterion.field];const observed=actual===undefined?{}:{[contract.criterion.field]:actual};return {resultDigestSha256:sha(observed),criteria:[{id:contract.criterion.id,passed:actual===contract.criterion.expected}]};}
@@ -230,6 +238,8 @@ export async function checkWitnoraProviderHealth(context){for(const contract of
230
238
  `;
231
239
  }
232
240
  async function persistProviderCredential(repository, plan, environment) {
241
+ if (plan.generated.providerPackId === "SHOPIFY_DISPUTE" && plan.environment === "sandbox" && plan.generated.providerConfiguration === undefined)
242
+ return;
233
243
  const name = providerCredentialEnvironmentName(plan.generated.providerPackId);
234
244
  const value = environment[name];
235
245
  if (!value)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "witnora",
3
- "version": "0.18.2",
3
+ "version": "0.18.4",
4
4
  "description": "Independent assurance for covered agent action paths across models and frameworks.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",