witnora 0.13.7 → 0.13.9

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.
@@ -0,0 +1,269 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { uploadTaskEvaluationReport } from "./task-evaluation-hosted.js";
5
+ export function createManagedWorkflowHostedClient(options) {
6
+ const baseUrl = required(options.baseUrl, "baseUrl").replace(/\/$/, "");
7
+ const projectId = required(options.projectId, "projectId");
8
+ const apiKey = required(options.apiKey, "apiKey");
9
+ const requestFetch = options.fetch ?? fetch;
10
+ const projectUrl = `${baseUrl}/v1/projects/${encodeURIComponent(projectId)}`;
11
+ const workflowIds = options.workflowIds?.map((id) => required(id, "workflowIds[]"));
12
+ if (workflowIds && (!workflowIds.length || workflowIds.length > 100 || new Set(workflowIds).size !== workflowIds.length)) {
13
+ throw new Error("Managed Workflow Harness workflowIds must contain 1 to 100 unique ids.");
14
+ }
15
+ return {
16
+ listActiveWorkflows: async () => {
17
+ const items = workflowIds
18
+ ? await Promise.all(workflowIds.map((id) => hostedJson(requestFetch, `${projectUrl}/business-workflows/${encodeURIComponent(id)}`, apiKey)))
19
+ : await (async () => {
20
+ const value = await hostedJson(requestFetch, `${projectUrl}/business-workflows`, apiKey);
21
+ if (!Array.isArray(value.businessWorkflows))
22
+ throw new Error("Witnora returned an invalid Business Workflow list.");
23
+ if (value.businessWorkflows.length >= 100)
24
+ throw new Error("Business Workflow listing reached its bounded limit; configure exact workflowIds before scheduling.");
25
+ return value.businessWorkflows;
26
+ })();
27
+ return items.map((item) => ({
28
+ id: required(String(item.id ?? ""), "workflow.id"),
29
+ digestSha256: digest(String(item.digestSha256 ?? ""), "workflow.digestSha256"),
30
+ status: item.status === "RETIRED" ? "RETIRED" : item.status === "ACTIVE" ? "ACTIVE" : invalidStatus(),
31
+ }));
32
+ },
33
+ getExecutionPlan: async (workflowId) => await hostedJson(requestFetch, `${projectUrl}/business-workflows/${encodeURIComponent(required(workflowId, "workflowId"))}/execution-plan`, apiKey),
34
+ getTask: async (taskContractId) => await hostedJson(requestFetch, `${projectUrl}/business-tasks/${encodeURIComponent(required(taskContractId, "taskContractId"))}`, apiKey),
35
+ upload: async (report, input) => await uploadTaskEvaluationReport(report, {
36
+ baseUrl,
37
+ projectId,
38
+ apiKey,
39
+ externalId: input.externalId,
40
+ fetch: requestFetch,
41
+ }),
42
+ };
43
+ }
44
+ export class MemoryWorkflowEvaluationCheckpointStore {
45
+ #reports = new Map();
46
+ async load(key) {
47
+ const report = this.#reports.get(key);
48
+ return report ? structuredClone(report) : undefined;
49
+ }
50
+ async save(key, report) {
51
+ this.#reports.set(key, structuredClone(report));
52
+ }
53
+ async complete(key) {
54
+ this.#reports.delete(key);
55
+ }
56
+ }
57
+ export class FileWorkflowEvaluationCheckpointStore {
58
+ directory;
59
+ constructor(directory) {
60
+ this.directory = directory;
61
+ if (!directory.trim())
62
+ throw new Error("Workflow checkpoint directory is required.");
63
+ }
64
+ async load(key) {
65
+ try {
66
+ return JSON.parse(await readFile(this.path(key), "utf8"));
67
+ }
68
+ catch (error) {
69
+ if (error.code === "ENOENT")
70
+ return undefined;
71
+ throw error;
72
+ }
73
+ }
74
+ async save(key, report) {
75
+ await mkdir(this.directory, { recursive: true });
76
+ const target = this.path(key);
77
+ const temporary = `${target}.${randomUUID()}.tmp`;
78
+ await writeFile(temporary, `${JSON.stringify(report)}\n`, { encoding: "utf8", mode: 0o600, flag: "wx" });
79
+ await rename(temporary, target);
80
+ }
81
+ async complete(key) {
82
+ await rm(this.path(key), { force: true });
83
+ }
84
+ path(key) {
85
+ return join(this.directory, `${createHash("sha256").update(key).digest("hex")}.json`);
86
+ }
87
+ }
88
+ export class ManagedBusinessWorkflowHarness {
89
+ #client;
90
+ #checkpoints;
91
+ #evaluate;
92
+ #maxConcurrency;
93
+ #activeTick;
94
+ constructor(options) {
95
+ this.#client = options.client;
96
+ this.#checkpoints = options.checkpoints;
97
+ this.#evaluate = options.evaluate;
98
+ this.#maxConcurrency = options.maxConcurrency ?? 4;
99
+ if (!Number.isSafeInteger(this.#maxConcurrency) || this.#maxConcurrency < 1 || this.#maxConcurrency > 16) {
100
+ throw new Error("Managed Workflow Harness maxConcurrency must be between 1 and 16.");
101
+ }
102
+ }
103
+ tick() {
104
+ if (this.#activeTick)
105
+ return this.#activeTick;
106
+ this.#activeTick = this.runTick().finally(() => { this.#activeTick = undefined; });
107
+ return this.#activeTick;
108
+ }
109
+ async runTick() {
110
+ const workflows = (await this.#client.listActiveWorkflows()).filter((workflow) => workflow.status === "ACTIVE");
111
+ let evaluationsCompleted = 0;
112
+ let evaluationsFailed = 0;
113
+ const limitations = [];
114
+ for (const workflow of workflows) {
115
+ const seen = new Set();
116
+ for (let wave = 0; wave < 100; wave += 1) {
117
+ const plan = await this.#client.getExecutionPlan(workflow.id);
118
+ validatePlan(plan, workflow);
119
+ if (plan.recordsTruncated) {
120
+ limitations.push(`Workflow ${workflow.id} was not scheduled because its authoritative Run inspection was truncated.`);
121
+ break;
122
+ }
123
+ const ready = plan.readyEvaluations.filter((evaluation) => !seen.has(checkpointKey(plan, evaluation)));
124
+ if (!ready.length)
125
+ break;
126
+ const results = await mapLimit(ready, this.#maxConcurrency, async (evaluation) => {
127
+ const key = checkpointKey(plan, evaluation);
128
+ seen.add(key);
129
+ try {
130
+ const task = await this.#client.getTask(evaluation.taskContractId);
131
+ validateTask(task, evaluation);
132
+ let report = await this.#checkpoints.load(key);
133
+ if (!report) {
134
+ report = await this.#evaluate({ task: structuredClone(task), evaluation: structuredClone(evaluation) });
135
+ validateReport(report, task, evaluation);
136
+ await this.#checkpoints.save(key, report);
137
+ }
138
+ else {
139
+ validateReport(report, task, evaluation);
140
+ }
141
+ await this.#client.upload(report, { externalId: externalId(plan, evaluation) });
142
+ await this.#checkpoints.complete(key);
143
+ return true;
144
+ }
145
+ catch {
146
+ limitations.push(`Workflow ${workflow.id} evaluation ${evaluation.nodeId}:${evaluation.mode} failed closed; no dependent evaluation was scheduled.`);
147
+ return false;
148
+ }
149
+ });
150
+ evaluationsCompleted += results.filter(Boolean).length;
151
+ evaluationsFailed += results.filter((value) => !value).length;
152
+ if (results.some((value) => !value))
153
+ break;
154
+ }
155
+ }
156
+ return { workflowsInspected: workflows.length, evaluationsCompleted, evaluationsFailed, limitations };
157
+ }
158
+ }
159
+ function validatePlan(plan, workflow) {
160
+ if (plan.schemaVersion !== "witnora.business_workflow_execution_plan.v0.1"
161
+ || plan.workflow.id !== workflow.id
162
+ || plan.workflow.digestSha256 !== workflow.digestSha256
163
+ || plan.workflow.status !== "ACTIVE"
164
+ || plan.safety.productionWrites !== 0
165
+ || plan.safety.aggregateAssuranceClaim !== false
166
+ || plan.safety.customerHarnessRequired !== true) {
167
+ throw new Error("Managed Workflow Harness rejected a mismatched or unsafe execution plan.");
168
+ }
169
+ if (plan.readyEvaluations.length > 200)
170
+ throw new Error("Managed Workflow Harness rejected an oversized execution plan.");
171
+ const nodeStatus = new Map(plan.nodes.map((node) => [node.id, node.status]));
172
+ const seen = new Set();
173
+ for (const evaluation of plan.readyEvaluations) {
174
+ const key = `${evaluation.nodeId}:${evaluation.mode}`;
175
+ if (seen.has(key)
176
+ || nodeStatus.get(evaluation.nodeId) !== "READY"
177
+ || (evaluation.mode !== "REPLAY" && evaluation.mode !== "SHADOW")
178
+ || !evaluation.taskContractId
179
+ || !evaluation.taskKey
180
+ || !evaluation.agentId
181
+ || !evaluation.agentVersion
182
+ || !/^[a-f0-9]{64}$/.test(evaluation.taskContractDigestSha256)) {
183
+ throw new Error("Managed Workflow Harness rejected an invalid ready evaluation.");
184
+ }
185
+ seen.add(key);
186
+ }
187
+ }
188
+ function validateTask(task, evaluation) {
189
+ if (task.id !== evaluation.taskContractId
190
+ || task.digestSha256 !== evaluation.taskContractDigestSha256
191
+ || task.taskKey !== evaluation.taskKey
192
+ || task.subject.agentId !== evaluation.agentId
193
+ || task.subject.agentVersion !== evaluation.agentVersion) {
194
+ throw new Error("Managed Workflow Harness rejected a Business Task that did not match the execution plan.");
195
+ }
196
+ }
197
+ function validateReport(report, task, evaluation) {
198
+ if (report.schemaVersion !== "witnora.task_evaluation.v0.1"
199
+ || report.mode !== evaluation.mode
200
+ || report.task.id !== task.id
201
+ || report.task.digestSha256 !== task.digestSha256
202
+ || report.task.subject.agentId !== task.subject.agentId
203
+ || report.task.subject.agentVersion !== task.subject.agentVersion
204
+ || report.summary.productionWrites !== 0
205
+ || (report.mode === "REPLAY" && report.classification !== "REPLAY_SANDBOX_ONLY")
206
+ || (report.mode === "SHADOW" && report.classification !== "SHADOW_NO_WRITE")) {
207
+ throw new Error("Managed Workflow Harness rejected a mismatched or unsafe evaluation report.");
208
+ }
209
+ exactKeys(report, ["schemaVersion", "mode", "classification", "generatedAt", "task", "verdict", "summary", "scenarios", "limitations"], "report");
210
+ exactKeys(report.task, ["id", "digestSha256", "taskKey", "version", "subject", "scenarioSuite"], "report.task");
211
+ exactKeys(report.task.subject, ["agentId", "agentVersion"], "report.task.subject");
212
+ exactKeys(report.task.scenarioSuite, ["id", "version", "sha256"], "report.task.scenarioSuite");
213
+ exactKeys(report.summary, ["scenarios", "matched", "changed", "blocked", "sandboxActions", "productionWrites"], "report.summary");
214
+ if (!Array.isArray(report.scenarios) || !Array.isArray(report.limitations) || report.limitations.some((value) => typeof value !== "string")) {
215
+ throw new Error("Managed Workflow Harness rejected a non-structured evaluation report.");
216
+ }
217
+ for (const scenario of report.scenarios) {
218
+ exactKeys(scenario, ["id", "source", "inputDigestSha256", "matched", "blocked", "sandboxActionCount", "criteria", "differences"], "report.scenarios[]");
219
+ if (!/^[a-f0-9]{64}$/.test(scenario.inputDigestSha256) || !Array.isArray(scenario.criteria) || !Array.isArray(scenario.differences)
220
+ || scenario.differences.some((value) => typeof value !== "string"))
221
+ throw new Error("Managed Workflow Harness rejected invalid scenario metadata.");
222
+ for (const criterion of scenario.criteria)
223
+ exactKeys(criterion, ["id", "status", "evaluator"], "report.scenarios[].criteria[]");
224
+ }
225
+ }
226
+ function exactKeys(value, allowed, name) {
227
+ const allowedKeys = new Set(allowed);
228
+ if (Object.keys(value).some((key) => !allowedKeys.has(key)))
229
+ throw new Error(`Managed Workflow Harness rejected unexpected ${name} data.`);
230
+ }
231
+ function checkpointKey(plan, evaluation) {
232
+ return `${plan.projectId}:${plan.workflow.id}:${plan.workflow.digestSha256}:${evaluation.nodeId}:${evaluation.taskContractDigestSha256}:${evaluation.mode}`;
233
+ }
234
+ function externalId(plan, evaluation) {
235
+ return `workflow:${plan.workflow.id}:${plan.workflow.digestSha256.slice(0, 16)}:${evaluation.nodeId}:${evaluation.mode.toLowerCase()}`;
236
+ }
237
+ async function mapLimit(items, limit, execute) {
238
+ const results = new Array(items.length);
239
+ let cursor = 0;
240
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, async () => {
241
+ while (cursor < items.length) {
242
+ const index = cursor++;
243
+ results[index] = await execute(items[index]);
244
+ }
245
+ }));
246
+ return results;
247
+ }
248
+ async function hostedJson(requestFetch, url, apiKey) {
249
+ const response = await requestFetch(url, { headers: { authorization: `Bearer ${apiKey}` }, redirect: "error" });
250
+ const value = await response.json().catch(() => ({}));
251
+ if (!response.ok)
252
+ throw new Error(typeof value.error === "string" ? value.error : `Witnora Control Plane request failed (${response.status}).`);
253
+ return value;
254
+ }
255
+ function required(value, name) {
256
+ const normalized = value.trim();
257
+ if (!normalized)
258
+ throw new Error(`Managed Workflow Harness ${name} is required.`);
259
+ return normalized;
260
+ }
261
+ function digest(value, name) {
262
+ const normalized = value.trim().toLowerCase();
263
+ if (!/^[a-f0-9]{64}$/.test(normalized))
264
+ throw new Error(`Managed Workflow Harness ${name} must be a SHA-256 digest.`);
265
+ return normalized;
266
+ }
267
+ function invalidStatus() {
268
+ throw new Error("Witnora returned an invalid Business Workflow status.");
269
+ }
@@ -0,0 +1,83 @@
1
+ import type { TaskEvaluationReport } from "./task-evaluation.js";
2
+ export interface TaskEvaluationHostedOptions {
3
+ baseUrl: string;
4
+ projectId: string;
5
+ apiKey: string;
6
+ externalId?: string;
7
+ fetch?: typeof fetch;
8
+ }
9
+ export interface TaskEvaluationHostedResult {
10
+ run: Record<string, unknown>;
11
+ evidence: Record<string, unknown>;
12
+ completion: Record<string, unknown>;
13
+ }
14
+ export declare function uploadTaskEvaluationReport(report: TaskEvaluationReport, options: TaskEvaluationHostedOptions): Promise<TaskEvaluationHostedResult>;
15
+ export declare function createTaskEvaluationEvidenceBundle(report: TaskEvaluationReport, runId: string, reportDigestSha256?: string): {
16
+ schemaName: string;
17
+ schemaVersion: string;
18
+ schemaSemver: string;
19
+ kind: string;
20
+ runId: string;
21
+ generatedAt: string;
22
+ subject: {
23
+ name: string;
24
+ type: string;
25
+ };
26
+ verdict: {
27
+ passed: boolean;
28
+ score: number;
29
+ level: "REPLAY_SANDBOX_ONLY" | "SHADOW_NO_WRITE";
30
+ };
31
+ summary: {
32
+ products: string[];
33
+ criticalEvidence: number;
34
+ highEvidence: number;
35
+ totalEvidence: number;
36
+ };
37
+ results: {
38
+ schemaVersion: string;
39
+ product: string;
40
+ runId: string;
41
+ timestamp: string;
42
+ phase: string;
43
+ score: number;
44
+ passed: boolean;
45
+ summary: string;
46
+ artifacts: {};
47
+ evidence: {
48
+ id: string;
49
+ kind: string;
50
+ severity: string;
51
+ message: string;
52
+ source: string;
53
+ metadata: {
54
+ report: TaskEvaluationReport;
55
+ reportSha256: string;
56
+ };
57
+ }[];
58
+ }[];
59
+ evidence: {
60
+ id: string;
61
+ kind: string;
62
+ severity: string;
63
+ message: string;
64
+ source: string;
65
+ metadata: {
66
+ reportSha256: string;
67
+ taskContractId: string;
68
+ classification: "REPLAY_SANDBOX_ONLY" | "SHADOW_NO_WRITE";
69
+ };
70
+ }[];
71
+ artifacts: {};
72
+ artifactManifest: {
73
+ schemaVersion: string;
74
+ entries: never[];
75
+ };
76
+ standards: {
77
+ id: string;
78
+ name: string;
79
+ status: string;
80
+ note: string;
81
+ }[];
82
+ };
83
+ //# sourceMappingURL=task-evaluation-hosted.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-evaluation-hosted.d.ts","sourceRoot":"","sources":["../../../../onegent-runtime/src/task-evaluation-hosted.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,wBAAsB,0BAA0B,CAC9C,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,0BAA0B,CAAC,CAyDrC;AAED,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+B1H"}
@@ -0,0 +1,108 @@
1
+ import { createHash } from "node:crypto";
2
+ export async function uploadTaskEvaluationReport(report, options) {
3
+ const baseUrl = required(options.baseUrl, "baseUrl").replace(/\/$/, "");
4
+ const projectId = required(options.projectId, "projectId");
5
+ const apiKey = required(options.apiKey, "apiKey");
6
+ const requestFetch = options.fetch ?? fetch;
7
+ const projectUrl = `${baseUrl}/v1/projects/${encodeURIComponent(projectId)}`;
8
+ const reportBytes = Buffer.from(`${JSON.stringify(report)}\n`, "utf8");
9
+ const reportDigestSha256 = createHash("sha256").update(reportBytes).digest("hex");
10
+ const operationId = `task-evaluation-${reportDigestSha256.slice(0, 32)}`;
11
+ const externalId = options.externalId?.trim() || `${report.mode.toLowerCase()}:${report.task.taskKey}:${report.task.version}:${reportDigestSha256.slice(0, 16)}`;
12
+ const run = await jsonRequest(requestFetch, `${projectUrl}/runs`, apiKey, {
13
+ method: "POST",
14
+ headers: { "idempotency-key": `${operationId}:run` },
15
+ body: JSON.stringify({
16
+ externalId,
17
+ kind: "custom",
18
+ agentId: report.task.subject.agentId,
19
+ schemaVersion: report.schemaVersion,
20
+ startedAt: report.generatedAt,
21
+ metadata: { taskEvaluation: {
22
+ taskContractId: report.task.id,
23
+ taskContractDigestSha256: report.task.digestSha256,
24
+ scenarioSuiteSha256: report.task.scenarioSuite.sha256,
25
+ reportDigestSha256,
26
+ mode: report.mode,
27
+ writesBlocked: true,
28
+ rawInputsRetainedByCustomer: true,
29
+ } },
30
+ }),
31
+ });
32
+ if (typeof run.id !== "string" || !run.id)
33
+ throw new Error("Witnora Control Plane returned a task evaluation Run without an id.");
34
+ const bundle = createTaskEvaluationEvidenceBundle(report, run.id, reportDigestSha256);
35
+ const bundleBytes = Buffer.from(`${JSON.stringify(bundle, null, 2)}\n`, "utf8");
36
+ const query = new URLSearchParams({
37
+ fileName: `business-task-${report.mode.toLowerCase()}-evidence.json`,
38
+ kind: "evidence_bundle",
39
+ schemaVersion: "agentcert.evidence.v0.1",
40
+ runId: run.id,
41
+ });
42
+ const evidence = await jsonRequest(requestFetch, `${projectUrl}/evidence?${query}`, apiKey, {
43
+ method: "POST",
44
+ headers: { "content-type": "application/json", "idempotency-key": `${operationId}:evidence` },
45
+ body: new Uint8Array(bundleBytes).buffer,
46
+ });
47
+ const passed = report.verdict === "PASSED" || report.verdict === "MATCHED";
48
+ const completion = await jsonRequest(requestFetch, `${projectUrl}/runs/${encodeURIComponent(run.id)}/complete`, apiKey, {
49
+ method: "POST",
50
+ headers: { "idempotency-key": `${operationId}:complete` },
51
+ body: JSON.stringify({
52
+ status: passed ? "passed" : "manual_review",
53
+ score: report.summary.scenarios ? report.summary.matched / report.summary.scenarios : 0,
54
+ summary: `${report.mode} evaluated ${report.summary.scenarios} declared scenario${report.summary.scenarios === 1 ? "" : "s"}; ${report.summary.changed} changed and ${report.summary.blocked} blocked.`,
55
+ metadata: { evidenceId: evidence.id, evidenceSha256: evidence.sha256, taskEvaluationReportSha256: reportDigestSha256 },
56
+ }),
57
+ });
58
+ return { run, evidence, completion };
59
+ }
60
+ export function createTaskEvaluationEvidenceBundle(report, runId, reportDigestSha256) {
61
+ const digest = reportDigestSha256 ?? createHash("sha256").update(`${JSON.stringify(report)}\n`).digest("hex");
62
+ const passed = report.verdict === "PASSED" || report.verdict === "MATCHED";
63
+ return {
64
+ schemaName: "agentcert.evidence_bundle",
65
+ schemaVersion: "agentcert.evidence.v0.1",
66
+ schemaSemver: "0.1.0",
67
+ kind: "agentcert.evidence_bundle",
68
+ runId,
69
+ generatedAt: report.generatedAt,
70
+ subject: { name: `${report.task.taskKey} v${report.task.version}`, type: "application" },
71
+ verdict: { passed, score: passed ? 100 : 0, level: report.classification },
72
+ summary: { products: ["onegent-runtime"], criticalEvidence: report.summary.blocked, highEvidence: report.summary.changed, totalEvidence: report.summary.scenarios },
73
+ results: [{
74
+ schemaVersion: "1", product: "onegent-runtime", runId, timestamp: report.generatedAt, phase: "pre-release",
75
+ score: passed ? 100 : 0, passed,
76
+ summary: `${report.mode} comparison for the declared Business Task Contract.`, artifacts: {},
77
+ evidence: [{
78
+ id: `task-evaluation:${digest.slice(0, 16)}`, kind: "task_evaluation", severity: passed ? "info" : "high",
79
+ message: `${report.mode} ${report.verdict.toLowerCase()} for ${report.summary.scenarios} declared scenario${report.summary.scenarios === 1 ? "" : "s"}.`,
80
+ source: "onegent-runtime", metadata: { report: structuredClone(report), reportSha256: digest },
81
+ }],
82
+ }],
83
+ evidence: [{
84
+ id: `task-evaluation:${digest.slice(0, 16)}`, kind: "task_evaluation", severity: passed ? "info" : "high",
85
+ message: `${report.mode} ${report.verdict.toLowerCase()} for the declared Business Task Contract.`, source: "onegent-runtime",
86
+ metadata: { reportSha256: digest, taskContractId: report.task.id, classification: report.classification },
87
+ }],
88
+ artifacts: {}, artifactManifest: { schemaVersion: "agentcert.artifact_manifest.v0.1", entries: [] },
89
+ standards: [{ id: "witnora-business-task", name: "Witnora Business Task Contract", status: "mapped", note: report.limitations.join(" ") }],
90
+ };
91
+ }
92
+ async function jsonRequest(requestFetch, url, apiKey, init) {
93
+ const headers = new Headers(init.headers);
94
+ headers.set("authorization", `Bearer ${apiKey}`);
95
+ if (typeof init.body === "string")
96
+ headers.set("content-type", "application/json");
97
+ const response = await requestFetch(url, { ...init, headers, redirect: "error" });
98
+ const value = await response.json().catch(() => ({}));
99
+ if (!response.ok)
100
+ throw new Error(typeof value.error === "string" ? value.error : `Witnora Control Plane request failed (${response.status}).`);
101
+ return value;
102
+ }
103
+ function required(value, name) {
104
+ const normalized = value.trim();
105
+ if (!normalized)
106
+ throw new Error(`Task evaluation hosted upload ${name} is required.`);
107
+ return normalized;
108
+ }
@@ -0,0 +1,123 @@
1
+ import type { SandboxCertificationHarness } from "./sandbox-harness.js";
2
+ import type { CreateActionIntentInput } from "./types.js";
3
+ export declare const TASK_EVALUATION_SCHEMA_VERSION: "witnora.task_evaluation.v0.1";
4
+ export interface TaskEvaluationContract {
5
+ id: string;
6
+ digestSha256: string;
7
+ taskKey: string;
8
+ version: number;
9
+ subject: {
10
+ agentId: string;
11
+ agentVersion: string;
12
+ };
13
+ scenarioSuite: {
14
+ id: string;
15
+ version: string;
16
+ sha256: string;
17
+ };
18
+ actionPaths: Array<{
19
+ id: string;
20
+ actionType: "SUBMIT" | "PAY" | "SEND" | "UPDATE";
21
+ targetSystem: string;
22
+ requiredPermissions: string[];
23
+ customerDecision?: "ALLOW" | "REQUIRE_APPROVAL" | "DENY";
24
+ }>;
25
+ successCriteria: Array<{
26
+ id: string;
27
+ evaluator: "DETERMINISTIC" | "READ_ONLY_PROBE";
28
+ predicateDigestSha256: string;
29
+ }>;
30
+ }
31
+ export interface TaskEvaluationIntent {
32
+ pathId: string;
33
+ parametersDigestSha256: string;
34
+ action?: CreateActionIntentInput;
35
+ }
36
+ export interface TaskEvaluationScenario {
37
+ id: string;
38
+ source: "HISTORICAL" | "FIXTURE" | "LIVE_SHADOW";
39
+ sanitized: boolean;
40
+ input: unknown;
41
+ inputDigestSha256: string;
42
+ baseline: {
43
+ resultDigestSha256: string;
44
+ actionIntents: Array<Omit<TaskEvaluationIntent, "action">>;
45
+ };
46
+ }
47
+ export interface TaskCandidateEvaluation {
48
+ resultDigestSha256: string;
49
+ actionIntents: TaskEvaluationIntent[];
50
+ criteria: Array<{
51
+ id: string;
52
+ passed: boolean;
53
+ }>;
54
+ }
55
+ export interface ShadowCandidateEvaluation {
56
+ resultDigestSha256: string;
57
+ criteria: Array<{
58
+ id: string;
59
+ passed: boolean;
60
+ }>;
61
+ }
62
+ export interface TaskEvaluationReport {
63
+ schemaVersion: typeof TASK_EVALUATION_SCHEMA_VERSION;
64
+ mode: "REPLAY" | "SHADOW";
65
+ classification: "REPLAY_SANDBOX_ONLY" | "SHADOW_NO_WRITE";
66
+ generatedAt: string;
67
+ task: {
68
+ id: string;
69
+ digestSha256: string;
70
+ taskKey: string;
71
+ version: number;
72
+ subject: TaskEvaluationContract["subject"];
73
+ scenarioSuite: TaskEvaluationContract["scenarioSuite"];
74
+ };
75
+ verdict: "PASSED" | "MATCHED" | "CHANGED" | "BLOCKED";
76
+ summary: {
77
+ scenarios: number;
78
+ matched: number;
79
+ changed: number;
80
+ blocked: number;
81
+ sandboxActions: number;
82
+ productionWrites: 0;
83
+ };
84
+ scenarios: Array<{
85
+ id: string;
86
+ source: TaskEvaluationScenario["source"];
87
+ inputDigestSha256: string;
88
+ matched: boolean;
89
+ blocked: boolean;
90
+ sandboxActionCount: number;
91
+ criteria: Array<{
92
+ id: string;
93
+ status: "PASSED" | "FAILED" | "NOT_OBSERVED";
94
+ evaluator: "DETERMINISTIC" | "READ_ONLY_PROBE";
95
+ }>;
96
+ differences: string[];
97
+ }>;
98
+ limitations: string[];
99
+ }
100
+ export declare function runReplayEvaluation(options: {
101
+ task: TaskEvaluationContract;
102
+ harness: SandboxCertificationHarness;
103
+ tenantId: string;
104
+ scenarios: TaskEvaluationScenario[];
105
+ evaluateCandidate: (scenario: {
106
+ id: string;
107
+ source: TaskEvaluationScenario["source"];
108
+ input: unknown;
109
+ }) => Promise<TaskCandidateEvaluation>;
110
+ now?: () => Date;
111
+ }): Promise<TaskEvaluationReport>;
112
+ export declare function runShadowEvaluation(options: {
113
+ task: TaskEvaluationContract;
114
+ observations: TaskEvaluationScenario[];
115
+ evaluateCandidate: (context: {
116
+ id: string;
117
+ source: "LIVE_SHADOW";
118
+ input: unknown;
119
+ propose: (intent: Omit<TaskEvaluationIntent, "action">) => void;
120
+ }) => Promise<ShadowCandidateEvaluation>;
121
+ now?: () => Date;
122
+ }): Promise<TaskEvaluationReport>;
123
+ //# sourceMappingURL=task-evaluation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-evaluation.d.ts","sourceRoot":"","sources":["../../../../onegent-runtime/src/task-evaluation.ts"],"names":[],"mappings":"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"}