witnora 0.17.0 → 0.18.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/gateway.js CHANGED
@@ -635,8 +635,13 @@ export async function createConfiguredWorkflowHarness(input) {
635
635
  let closing = false;
636
636
  let lastTickAt;
637
637
  let lastError;
638
+ let lastProviderHealthAt = 0;
638
639
  const tick = async () => {
639
640
  try {
641
+ if (managedEvaluator?.healthCheck && Date.now() - lastProviderHealthAt >= 30_000) {
642
+ lastProviderHealthAt = Date.now();
643
+ await managedEvaluator.healthCheck();
644
+ }
640
645
  const result = await worker.tick();
641
646
  lastTickAt = new Date().toISOString();
642
647
  lastError = (result.evaluationsFailed ?? 0) > 0 ? result.limitations?.[0] ?? "One or more evaluations failed closed." : undefined;
@@ -699,10 +704,11 @@ async function startManagedLocalEvaluator(directory, config, evaluatorKit) {
699
704
  if (typeof imported.createWitnoraBusinessTaskEvaluatorOptions !== "function") {
700
705
  throw new Error("Managed evaluator module did not export createWitnoraBusinessTaskEvaluatorOptions().");
701
706
  }
702
- const customerOptions = await imported.createWitnoraBusinessTaskEvaluatorOptions({
707
+ const evaluatorContext = {
703
708
  repository,
704
709
  dataDirectory: join(directory, "data", "business-task-evaluator"),
705
- });
710
+ };
711
+ const customerOptions = await imported.createWitnoraBusinessTaskEvaluatorOptions(evaluatorContext);
706
712
  if (!customerOptions || typeof customerOptions !== "object" || Array.isArray(customerOptions)) {
707
713
  throw new Error("Managed evaluator module returned invalid Evaluator Kit options.");
708
714
  }
@@ -715,7 +721,7 @@ async function startManagedLocalEvaluator(directory, config, evaluatorKit) {
715
721
  port: 0,
716
722
  });
717
723
  const started = await evaluator.start();
718
- return { origin: started.origin, credential, close: () => evaluator.close() };
724
+ return { origin: started.origin, credential, ...(imported.checkWitnoraProviderHealth ? { healthCheck: async () => { await imported.checkWitnoraProviderHealth(evaluatorContext); } } : {}), close: () => evaluator.close() };
719
725
  }
720
726
  async function runtimeSandboxFixtureReady(config) {
721
727
  try {
@@ -21,6 +21,7 @@ export async function activateRealPathIntegrations(options) {
21
21
  for (const plan of plans) {
22
22
  const preflight = await providerPreflight(request, environment, plan, options.now?.() ?? new Date(), options.postgresClientFactory);
23
23
  const acceptance = preflight.acceptance;
24
+ await persistProviderCredential(repository, plan, environment);
24
25
  activations.push({ integrationId: plan.id, integrationDigestSha256: plan.digestSha256, taskContractId: plan.taskContractId, taskContractDigestSha256: plan.taskContractDigestSha256, agentId: plan.subject.agentId, agentVersion: plan.subject.agentVersion, environment: plan.environment, providerPackId: plan.generated.providerPackId, providerContractDigestSha256: plan.generated.providerContractDigestSha256, acceptance });
25
26
  scenarios.push({ plan, value: [preflight.scenario] });
26
27
  }
@@ -191,20 +192,48 @@ async function postgresPreflight(env, plan, now, clientFactory) {
191
192
  }
192
193
  async function defaultPostgresClient(connectionString) { const imported = await import("pg"); return new imported.Client({ connectionString, application_name: "witnora-read-only-preflight" }); }
193
194
  function generatedProviderHarness(plans) {
194
- const contracts = plans.map((plan) => ({ taskContractId: plan.taskContractId, providerPackId: plan.generated.providerPackId, providerConfiguration: plan.generated.providerConfiguration ?? {}, criterion: plan.generated.criterion, actionPathIds: plan.generated.actionPathBindings.map((item) => item.actionPathId) }));
195
+ 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) }));
195
196
  return `import {createHash} from "node:crypto";
196
197
  import {readFile} from "node:fs/promises";
197
198
  import {join} from "node:path";
198
199
  const contracts=${JSON.stringify(contracts)};
199
200
  const sha=(value)=>createHash("sha256").update(typeof value==="string"?value:JSON.stringify(value)).digest("hex");
200
201
  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();};
201
- const observe=async(contract,resourceId)=>{if(contract.providerPackId==="STRIPE_REFUND"){const key=process.env.STRIPE_SECRET_KEY;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==="ZENDESK_TICKET"){const key=process.env.ZENDESK_API_TOKEN;if(!key)throw new Error("Zendesk read-only credential is unavailable.");return (await request(contract.providerConfiguration.origin+"/api/v2/tickets/"+encodeURIComponent(resourceId)+".json",key)).ticket;}if(contract.providerPackId==="SALESFORCE_RECORD"){const key=process.env.SALESFORCE_ACCESS_TOKEN;if(!key)throw new Error("Salesforce read-only credential is unavailable.");return request(contract.providerConfiguration.origin+"/services/data/v61.0/sobjects/"+encodeURIComponent(contract.providerConfiguration.resourceType)+"/"+encodeURIComponent(resourceId),key);}if(contract.providerPackId==="HUBSPOT_CRM_RECORD"){const key=process.env.HUBSPOT_ACCESS_TOKEN;if(!key)throw new Error("HubSpot read-only credential is unavailable.");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"){const url=process.env.WITNORA_POSTGRES_READ_URL;if(!url)throw new Error("PostgreSQL read-only credential is unavailable.");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:url,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.");};
202
+ 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==="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.");};
202
203
  export function createWitnoraBusinessTaskEvaluatorOptions(context){return {
203
204
  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;},
204
- 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);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}]};}
205
+ 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}]};}
205
206
  };}
207
+ export async function checkWitnoraProviderHealth(context){for(const contract of contracts){const path=join(context.repository,".witnora","scenarios",contract.taskContractId+".json");const scenarios=JSON.parse(await readFile(path,"utf8"));const resourceId=String(scenarios?.[0]?.input?.resourceId??"");if(!/^[A-Za-z0-9._:-]{1,200}$/.test(resourceId))throw new Error("Provider health scenario is unavailable.");const raw=await observe(contract,resourceId,context.repository);if(raw?.[contract.criterion.field]===undefined)throw new Error("The approved Provider result field is no longer observable.");}return {ready:true,checked:contracts.length};}
206
208
  `;
207
209
  }
210
+ async function persistProviderCredential(repository, plan, environment) {
211
+ const name = providerCredentialEnvironmentName(plan.generated.providerPackId);
212
+ const value = environment[name];
213
+ if (!value)
214
+ throw new Error(`${plan.generated.providerPackId} activation requires its read-only credential in the customer environment.`);
215
+ const directory = join(repository, ".witnora", "provider-credentials");
216
+ const target = join(directory, `${plan.id}.secret`);
217
+ const temporary = `${target}.${randomUUID()}.tmp`;
218
+ await mkdir(directory, { recursive: true });
219
+ await writeFile(join(directory, ".gitignore"), "*\n!.gitignore\n", { encoding: "utf8", mode: 0o600, flag: "wx" }).catch((error) => { if (error.code !== "EEXIST")
220
+ throw error; });
221
+ await writeFile(temporary, value, { encoding: "utf8", mode: 0o600, flag: "wx" });
222
+ await rename(temporary, target);
223
+ }
224
+ function providerCredentialEnvironmentName(packId) {
225
+ if (packId === "STRIPE_REFUND")
226
+ return "STRIPE_SECRET_KEY";
227
+ if (packId === "ZENDESK_TICKET")
228
+ return "ZENDESK_API_TOKEN";
229
+ if (packId === "SALESFORCE_RECORD")
230
+ return "SALESFORCE_ACCESS_TOKEN";
231
+ if (packId === "HUBSPOT_CRM_RECORD")
232
+ return "HUBSPOT_ACCESS_TOKEN";
233
+ if (packId === "POSTGRES_RECORD")
234
+ return "WITNORA_POSTGRES_READ_URL";
235
+ throw new Error(`Provider credential mapping is not supported for ${packId}.`);
236
+ }
208
237
  function parsePlan(value) { if (!value || typeof value !== "object" || Array.isArray(value))
209
238
  throw new Error("Real-path integration response is invalid."); const plan = value; if (plan.schemaVersion !== "witnora.real_path_integration.v0.1" || !plan.id || !plan.taskContractId || !plan.subject?.agentId || !plan.subject.agentVersion || !plan.generated || plan.generated.boundaries?.rawPayloadUpload !== false || plan.generated.boundaries.rawCredentialUpload !== false || plan.generated.boundaries.evaluatorWrites !== false || plan.generated.boundaries.firstAcceptanceProductionWrites !== 0 || !digest(plan.digestSha256) || !digest(plan.taskContractDigestSha256) || !digest(plan.generated.providerContractDigestSha256))
210
239
  throw new Error("Real-path integration response failed its safety contract."); return plan; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "witnora",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Independent assurance for covered agent action paths across models and frameworks.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",