witnora 0.18.5 → 0.18.6

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
@@ -1260,20 +1260,33 @@ export async function activateManagedWorkflowHarness(options) {
1260
1260
  const path = join(directory, "workflow-harness.json");
1261
1261
  const serialized = `${JSON.stringify(config, null, 2)}\n`;
1262
1262
  let created = false;
1263
+ let changed = false;
1263
1264
  if (await exists(path)) {
1264
1265
  const current = await readFile(path, "utf8");
1265
1266
  if (current !== serialized) {
1266
1267
  const parsed = parseManagedWorkflowHarnessConfig(current);
1267
- if (parsed.schemaVersion !== MANAGED_WORKFLOW_HARNESS_SCHEMA || canonicalHarnessConfig(parsed) !== canonicalHarnessConfig(config))
1268
+ if (parsed.schemaVersion !== MANAGED_WORKFLOW_HARNESS_SCHEMA
1269
+ || (canonicalHarnessConfig(parsed) !== canonicalHarnessConfig(config)
1270
+ && !isExactGeneratedHarnessUpgrade(parsed, config, options.previousGeneratedModuleSha256))) {
1268
1271
  throw new Error("Existing workflow-harness.json differs from the generated Assurance Harness binding; refusing to overwrite customer configuration.");
1272
+ }
1269
1273
  await writeFile(path, serialized, { encoding: "utf8", mode: 0o600 });
1274
+ changed = true;
1270
1275
  }
1271
1276
  }
1272
1277
  else {
1273
1278
  await writeFile(path, serialized, { encoding: "utf8", mode: 0o600, flag: "wx" });
1274
1279
  created = true;
1280
+ changed = true;
1275
1281
  }
1276
- return { state: "READY_TO_START", path, modulePath: evaluatorModulePath, config, created };
1282
+ return { state: "READY_TO_START", path, modulePath: evaluatorModulePath, config, created, changed };
1283
+ }
1284
+ function isExactGeneratedHarnessUpgrade(current, next, previousGeneratedModuleSha256) {
1285
+ if (!previousGeneratedModuleSha256 || current.evaluatorModuleSha256 !== previousGeneratedModuleSha256 || current.evaluatorContractSha256 !== previousGeneratedModuleSha256)
1286
+ return false;
1287
+ const { realPathActivations: _currentActivations, evaluatorModuleSha256: _currentModule, evaluatorContractSha256: _currentContract, ...currentStable } = current;
1288
+ const { realPathActivations: _nextActivations, evaluatorModuleSha256: _nextModule, evaluatorContractSha256: _nextContract, ...nextStable } = next;
1289
+ return JSON.stringify(currentStable) === JSON.stringify(nextStable);
1277
1290
  }
1278
1291
  function canonicalHarnessConfig(value) {
1279
1292
  const { realPathActivations: _activations, ...stable } = value;
package/dist/onboard.js CHANGED
@@ -88,6 +88,7 @@ export async function runOnboard(options) {
88
88
  let gatewayMigration;
89
89
  let managedGateway;
90
90
  let runtimeUpgrade;
91
+ let realPathActivation;
91
92
  let assuranceHarnessReadiness = {
92
93
  state: "WAITING_FOR_CUSTOMER_HARNESS",
93
94
  limitation: "No approved customer Harness module is bound to this Agent repository.",
@@ -196,13 +197,13 @@ export async function runOnboard(options) {
196
197
  }
197
198
  const runtimeConfigured = await gatewayHasRuntimeWorker(repositoryPath);
198
199
  generatedFiles.push(...await generateAutopilotFiles(repositoryPath, repository.name, runtimeConfigured));
199
- const realPathActivation = await activateRealPathIntegrations({ repository: repositoryPath, server, projectId: token.projectId, apiKey: token.apiKey, env: options.env, fetch: requestFetch });
200
+ realPathActivation = await activateRealPathIntegrations({ repository: repositoryPath, server, projectId: token.projectId, apiKey: token.apiKey, env: options.env, fetch: requestFetch });
200
201
  generatedFiles.push(...realPathActivation.generatedFiles);
201
202
  try {
202
- const activation = await activateManagedWorkflowHarness({ repository: repositoryPath, realPathActivations: realPathActivation.activations });
203
+ const activation = await activateManagedWorkflowHarness({ repository: repositoryPath, realPathActivations: realPathActivation.activations, previousGeneratedModuleSha256: realPathActivation.previousGeneratedModuleSha256 });
203
204
  if (activation.created)
204
205
  generatedFiles.push(activation.path);
205
- assuranceHarnessChanged = activation.created;
206
+ assuranceHarnessChanged = activation.changed;
206
207
  assuranceHarnessReadiness = { state: "ACTIVE" };
207
208
  }
208
209
  catch (error) {
@@ -266,6 +267,7 @@ export async function runOnboard(options) {
266
267
  if (managedGateway?.started) {
267
268
  await gatewayLifecycle.stop({ repository: repositoryPath, configHome: options.configHome, fetch: requestFetch }).catch(() => undefined);
268
269
  }
270
+ await realPathActivation?.rollback().catch(() => undefined);
269
271
  await rollbackGeneratedFiles(generatedFiles);
270
272
  await runtimeUpgrade?.rollback().catch(() => undefined);
271
273
  if (gatewayMigration)
@@ -1,5 +1,5 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
  export async function activateRealPathIntegrations(options) {
5
5
  const repository = resolve(options.repository ?? process.cwd());
@@ -7,14 +7,14 @@ export async function activateRealPathIntegrations(options) {
7
7
  const base = options.server.replace(/\/$/, "");
8
8
  const response = await request(`${base}/v1/projects/${encodeURIComponent(options.projectId)}/real-path-integrations`, { headers: { authorization: `Bearer ${options.apiKey}` } });
9
9
  if (response.status === 404)
10
- return { state: "NOT_CONFIGURED", created: false, activations: [], generatedFiles: [] };
10
+ return { state: "NOT_CONFIGURED", created: false, activations: [], generatedFiles: [], rollback: async () => undefined };
11
11
  if (!response.ok)
12
12
  throw new Error(`Could not load approved real-path integrations (${response.status}).`);
13
13
  const body = await boundedJson(response);
14
14
  const approved = Array.isArray(body.integrations) ? body.integrations.map(parsePlan).filter((plan) => plan.status === "READY_TO_ACTIVATE") : [];
15
15
  const plans = approved.filter((plan) => ["STRIPE_REFUND", "SHOPIFY_DISPUTE", "ZENDESK_TICKET", "SALESFORCE_RECORD", "HUBSPOT_CRM_RECORD", "POSTGRES_RECORD"].includes(plan.generated.providerPackId) && plan.environment === "sandbox" && plan.customerSummary.evaluationMode === "SHADOW");
16
16
  if (!plans.length)
17
- return { state: "NOT_CONFIGURED", created: false, activations: [], generatedFiles: [] };
17
+ return { state: "NOT_CONFIGURED", created: false, activations: [], generatedFiles: [], rollback: async () => undefined };
18
18
  const environment = options.env ?? process.env;
19
19
  const activations = [];
20
20
  const scenarios = [];
@@ -32,7 +32,9 @@ export async function activateRealPathIntegrations(options) {
32
32
  let created = false;
33
33
  const generatedFiles = [];
34
34
  const current = await readFile(modulePath, "utf8").catch((error) => error.code === "ENOENT" ? undefined : Promise.reject(error));
35
- const prior = await readFile(manifestPath, "utf8").then((value) => JSON.parse(value)).catch(() => undefined);
35
+ const priorSource = await readFile(manifestPath, "utf8").catch((error) => error.code === "ENOENT" ? undefined : Promise.reject(error));
36
+ const prior = priorSource === undefined ? undefined : JSON.parse(priorSource);
37
+ const previousGeneratedModuleSha256 = current !== undefined && sha(current) === prior?.moduleDigestSha256 ? prior.moduleDigestSha256 : undefined;
36
38
  if (current === undefined) {
37
39
  await writeFile(modulePath, source, { encoding: "utf8", mode: 0o600, flag: "wx" });
38
40
  created = true;
@@ -44,12 +46,12 @@ export async function activateRealPathIntegrations(options) {
44
46
  const temporary = `${modulePath}.${randomUUID()}.tmp`;
45
47
  await writeFile(temporary, source, { encoding: "utf8", mode: 0o600, flag: "wx" });
46
48
  await rename(temporary, modulePath);
47
- generatedFiles.push(modulePath);
48
49
  }
49
50
  const manifest = { schemaVersion: "witnora.real_path_activation_manifest.v0.1", projectId: options.projectId, moduleDigestSha256, activations };
50
51
  await mkdir(dirname(manifestPath), { recursive: true });
51
52
  await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
52
- generatedFiles.push(manifestPath);
53
+ if (priorSource === undefined)
54
+ generatedFiles.push(manifestPath);
53
55
  for (const item of scenarios) {
54
56
  const path = join(repository, ".witnora", "scenarios", `${item.plan.taskContractId}.json`);
55
57
  await mkdir(dirname(path), { recursive: true });
@@ -62,7 +64,16 @@ export async function activateRealPathIntegrations(options) {
62
64
  throw error;
63
65
  }
64
66
  }
65
- return { state: "READY_TO_START", created, modulePath: "witnora.assurance-harness.mjs", activations, generatedFiles };
67
+ return { state: "READY_TO_START", created, modulePath: "witnora.assurance-harness.mjs", activations, generatedFiles, ...(previousGeneratedModuleSha256 ? { previousGeneratedModuleSha256 } : {}), rollback: async () => {
68
+ if (current === undefined)
69
+ await rm(modulePath, { force: true });
70
+ else
71
+ await writeFile(modulePath, current, { encoding: "utf8", mode: 0o600 });
72
+ if (priorSource === undefined)
73
+ await rm(manifestPath, { force: true });
74
+ else
75
+ await writeFile(manifestPath, priorSource, { encoding: "utf8", mode: 0o600 });
76
+ } };
66
77
  }
67
78
  async function providerPreflight(request, env, plan, now, postgresClientFactory) {
68
79
  if (plan.generated.providerPackId === "STRIPE_REFUND") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "witnora",
3
- "version": "0.18.5",
3
+ "version": "0.18.6",
4
4
  "description": "Independent assurance for covered agent action paths across models and frameworks.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",