@cronch/kernel 0.0.0-stage → 0.12.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/index.js ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The kernel, compiled to WebAssembly (`crates/kernel-abi`). Requests and responses cross in deterministic CBOR.
3
+ */
4
+
5
+ import { decode, encode } from "./cbor.js";
6
+
7
+ export { decode, EncodeError, encode } from "./cbor.js";
8
+
9
+ /** The kernel refused a request: it was not one it understands. A bug in the caller, never a verdict. */
10
+ export class KernelError extends Error {
11
+ name = "KernelError";
12
+
13
+ /** @param {unknown} detail */
14
+ constructor(detail) {
15
+ super(`The kernel refused the request: ${JSON.stringify(detail)}`);
16
+ this.detail = detail;
17
+ }
18
+ }
19
+
20
+ /** @param {BufferSource | WebAssembly.Module} source */
21
+ export async function instantiate(source) {
22
+ // A Worker can't compile bytes at runtime; it imports a compiled module instead.
23
+ const instance =
24
+ source instanceof WebAssembly.Module
25
+ ? await WebAssembly.instantiate(source, {})
26
+ : (await WebAssembly.instantiate(source, {})).instance;
27
+ const exports = instance.exports;
28
+
29
+ const call = (request) => {
30
+ const bytes = encode(request);
31
+ const pointer = exports.cronch_alloc(bytes.length);
32
+ new Uint8Array(exports.memory.buffer, pointer, bytes.length).set(bytes);
33
+ const packed = exports.cronch_call(pointer, bytes.length);
34
+ const outPointer = Number(packed >> 32n);
35
+ const outLength = Number(packed & 0xffffffffn);
36
+ // Copy out before freeing: the call may have grown, and so replaced, the memory buffer.
37
+ const out = new Uint8Array(exports.memory.buffer, outPointer, outLength).slice();
38
+ exports.cronch_free(outPointer, outLength);
39
+ const decoded = decode(out);
40
+ if (!decoded.ok) throw new KernelError({ kind: "undecodableResponse", error: decoded.error });
41
+ const response = decoded.value;
42
+ if (!response.ok) throw new KernelError(response.error);
43
+ return response.result;
44
+ };
45
+
46
+ return {
47
+ infer: (expr, vars, expected) =>
48
+ call({ op: "infer", expr, vars: vars ?? {}, ...(expected ? { expected } : {}) }),
49
+ evaluate: (expr, vars) => call({ op: "evaluate", expr, vars: vars ?? {} }),
50
+ checkHarness: (harness, iface) =>
51
+ call({ op: "checkHarness", harness, interface: iface }).problems,
52
+ inputType: (harness, iface) => call({ op: "inputType", harness, interface: iface }).type,
53
+ actionType: (iface, result, spawns) =>
54
+ call({ op: "actionType", interface: iface, result, spawns: spawns ?? {} }).type,
55
+ checkInterface: (iface) => call({ op: "checkInterface", interface: iface }).problems,
56
+ install: (capabilities) => call({ op: "install", capabilities }),
57
+ narrow: (iface, narrowings) => call({ op: "narrow", interface: iface, narrowings }),
58
+ init: ({ harness, interface: iface }) => call({ op: "init", harness, interface: iface ?? {} }),
59
+ deliver: (system, delivery) => call({ op: "deliver", system, delivery }),
60
+ view: (events) => call({ op: "view", events }).events,
61
+ eventType: (harness, iface) => call({ op: "eventType", harness, interface: iface }).type,
62
+ checkProperty: (property, harness, iface) =>
63
+ call({ op: "checkProperty", property, harness, interface: iface }).problems,
64
+ monitor: (property, trace) => call({ op: "monitor", property, trace }),
65
+ monitorStart: (property) => call({ op: "monitorStart", property }),
66
+ monitorStep: (property, state, events) =>
67
+ call({ op: "monitorStep", property, state: state ?? {}, events }),
68
+ prove: (subject, method) => call({ op: "prove", ...subject, method }),
69
+ verify: (subject, evidence) => call({ op: "verify", ...subject, evidence }),
70
+ certificate: (subject, alphabet, limit) =>
71
+ call({ op: "certificate", ...subject, alphabet, limit: limit ?? 10000 }),
72
+ obligation: ({ harness, interface: iface, invariant, floats }) =>
73
+ call({
74
+ op: "obligation",
75
+ harness,
76
+ interface: iface ?? {},
77
+ invariant,
78
+ floats: floats ?? "exact",
79
+ }),
80
+ checkInduction: ({ harness, interface: iface, invariant, state, input }) =>
81
+ call({ op: "checkInduction", harness, interface: iface ?? {}, invariant, state, input }),
82
+ elaborate: (source, capabilities, harnesses) =>
83
+ call({ op: "elaborate", source, capabilities, harnesses: harnesses ?? {} }),
84
+ key: (kind, object) => call({ op: "objectKey", kind, object }).key,
85
+ smtlib: (definitions, floats) => call({ op: "smtlib", definitions, floats: floats ?? "exact" }),
86
+ ruleObligations: ({ interface: iface, rule, floats, format }) =>
87
+ call({
88
+ op: "ruleObligations",
89
+ interface: iface,
90
+ rule,
91
+ floats: floats ?? "exact",
92
+ format: format ?? "smtlib",
93
+ }),
94
+ readValues: (reply, types) => call({ op: "readValues", reply, types }),
95
+ };
96
+ }
package/kernel.sha256 ADDED
@@ -0,0 +1 @@
1
+ 36b02b56a03c4bd45afdcdf78d048fca345bdea60c1bed6998a6c70d56e92ee0
package/kernel.wasm ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,50 @@
1
1
  {
2
2
  "name": "@cronch/kernel",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.12.0",
4
+ "description": "The reference kernel for Pata (Proven Agents, Trusted Actions): agent harnesses whose safety can be proved",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/cronch-ai/kernel.git",
8
+ "directory": "javascript"
9
+ },
10
+ "homepage": "https://github.com/cronch-ai/kernel#readme",
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "type": "module",
15
+ "bin": {
16
+ "pata": "./cli.js"
17
+ },
18
+ "exports": {
19
+ ".": {
20
+ "types": "./index.d.ts",
21
+ "default": "./index.js"
22
+ },
23
+ "./kernel.wasm": "./kernel.wasm",
24
+ "./kernel.sha256": "./kernel.sha256",
25
+ "./run": {
26
+ "types": "./run.d.ts",
27
+ "default": "./run.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "cli.js",
32
+ "index.js",
33
+ "cbor.js",
34
+ "index.d.ts",
35
+ "run.js",
36
+ "run.d.ts",
37
+ "kernel.wasm",
38
+ "kernel.sha256",
39
+ "examples/claude-code/*.pata",
40
+ "examples/claude-code/*.json",
41
+ "LICENSE"
42
+ ],
43
+ "scripts": {
44
+ "build": "../scripts/build-wasm.sh",
45
+ "test": "node --test",
46
+ "prepack": "rm -rf examples && mkdir -p examples/claude-code && cp ../examples/claude-code/*.pata ../examples/claude-code/*.json examples/claude-code/",
47
+ "clean": "npm run clean --prefix .."
48
+ },
49
+ "license": "Apache-2.0"
50
+ }
package/run.d.ts ADDED
@@ -0,0 +1,67 @@
1
+ import type {
2
+ Delivery,
3
+ Event,
4
+ Harness,
5
+ Interface,
6
+ Kernel,
7
+ Label,
8
+ Outcome,
9
+ System,
10
+ Value,
11
+ } from "./index.js";
12
+
13
+ /** The `prev` of a log's first entry. */
14
+ export const GENESIS: string;
15
+
16
+ /** The error a call fails with when its implementation answered with a result the kernel withheld. */
17
+ export const NO_FIT: string;
18
+
19
+ /** The kernel this package ships, instantiated once. Node only. */
20
+ export function kernel(): Promise<Kernel>;
21
+
22
+ export interface CallContext {
23
+ /** The call's identifier, as the kernel numbered it. */
24
+ readonly call: number;
25
+ /** Aborted when the run cancels the call. The handler should still answer (H6). */
26
+ readonly signal: AbortSignal;
27
+ /** Deliver a message to the run on `channel`, as `prefix.channel`. */
28
+ send(channel: string, value: Value): void;
29
+ }
30
+
31
+ /** An operation's implementation: its result is the call's, and a throw fails the call. */
32
+ export type Handler = (args: Value, context: CallContext) => Promise<Value>;
33
+
34
+ export interface RunOptions {
35
+ /** The start's value. */
36
+ readonly input?: Value;
37
+ /** The sources the starting principal is no more trusted than. None: a trusted principal. */
38
+ readonly label?: Label;
39
+ readonly kernel?: Kernel;
40
+ /** For a term, in place of the interface installed from `capabilities`, each under its own name. */
41
+ readonly interface?: Interface;
42
+ }
43
+
44
+ export interface LogEntry {
45
+ readonly seq: number;
46
+ readonly prev: string;
47
+ readonly delivery: Delivery;
48
+ readonly events: readonly Event[];
49
+ /** The kernel's `logEntry` key of the entry without it. */
50
+ readonly key: string;
51
+ }
52
+
53
+ /**
54
+ * Run `harness`, Pata source or an elaborated term, to the end, with each operation (`prefix.op`) answered by its
55
+ * handler. A local host that keeps the host protocol: no network, no state beyond the run.
56
+ */
57
+ export function run(
58
+ harness: string | Harness,
59
+ capabilities: Readonly<Record<string, Interface>>,
60
+ handlers: Readonly<Record<string, Handler>>,
61
+ options?: RunOptions,
62
+ ): Promise<{
63
+ readonly outcome: Outcome | undefined;
64
+ readonly system: System;
65
+ readonly events: readonly Event[];
66
+ readonly log: readonly LogEntry[];
67
+ }>;
package/run.js ADDED
@@ -0,0 +1,145 @@
1
+ /**
2
+ * A local host: runs a harness against capabilities implemented by JavaScript functions, with no network and no state
3
+ * beyond the run. It keeps the host protocol (`docs/2026-11-06-host-protocol.md`); each duty is marked where it is
4
+ * kept.
5
+ */
6
+
7
+ import { readFile } from "node:fs/promises";
8
+ import { instantiate } from "./index.js";
9
+
10
+ /** The `prev` of a log's first entry. */
11
+ export const GENESIS = `sha256:${"0".repeat(64)}`;
12
+
13
+ /** The error a call fails with when its implementation answered with a result the kernel withheld. */
14
+ export const NO_FIT = "no result that fits";
15
+
16
+ let shared;
17
+
18
+ /** The kernel this package ships. */
19
+ export function kernel() {
20
+ shared ??= readFile(new URL("./kernel.wasm", import.meta.url)).then(instantiate);
21
+ return shared;
22
+ }
23
+
24
+ /**
25
+ * Runs `harness`, Pata source or an elaborated term, to the end.
26
+ *
27
+ * `capabilities` maps each capability's name, as `uses` names it, to its contract. `handlers` maps an operation, as
28
+ * `prefix.op`, to an async function of its arguments and `{ call, signal, send }`: its result is the call's, and a
29
+ * throw fails the call. `send(channel, value)` delivers a message to the run.
30
+ *
31
+ * Options: `input`, the start's value; `label`, the sources the starting principal is no more trusted than (none: a
32
+ * trusted principal); `kernel`, an instantiated kernel; `interface`, for a term, in place of one installed from
33
+ * `capabilities` with each under its own name.
34
+ *
35
+ * Resolves to the run's `outcome`, its final `system`, every event, and the `log`, hash-chained as `logEntry`s.
36
+ */
37
+ export async function run(harness, capabilities, handlers, options = {}) {
38
+ const k = options.kernel ?? (await kernel());
39
+ const { term, iface } = load(k, harness, capabilities, options.interface);
40
+
41
+ const ready = k.init({ harness: term, interface: iface });
42
+ if (ready.kind !== "ready") throw new Error(`init: ${JSON.stringify(ready)}`);
43
+ let system = ready.system;
44
+ const events = [];
45
+ const log = [];
46
+ const queue = [];
47
+ const inFlight = new Map();
48
+ let wake = () => {};
49
+
50
+ const enqueue = (delivery) => {
51
+ queue.push(delivery);
52
+ wake();
53
+ };
54
+
55
+ const perform = (event) => {
56
+ const { action } = event;
57
+ const id = event.call;
58
+ const handler = handlers[action.op];
59
+ const controller = new AbortController();
60
+ const send = (channel, value) => enqueue({ input: { kind: "msg", channel, value } });
61
+ const done = (async () => {
62
+ if (handler === undefined) throw new Error(`no handler for ${action.op}`);
63
+ return handler(action.args, { call: id, signal: controller.signal, send });
64
+ })().then(
65
+ (value) => ({ kind: "ret", call: id, value }),
66
+ (error) => ({ kind: "failed", call: id, error: String(error?.message ?? error) }),
67
+ );
68
+ inFlight.set(key(event.origin, id), controller);
69
+ // H8: every call ends, with its result or `failed`.
70
+ done.then((input) => {
71
+ inFlight.delete(key(event.origin, id));
72
+ enqueue(event.origin.length === 0 ? { input } : { to: event.origin, input });
73
+ });
74
+ };
75
+
76
+ const step = (delivery) => {
77
+ // H1, H3: each delivery steps the configuration the last one returned, unchanged, one at a time.
78
+ const out = k.deliver(system, delivery);
79
+ system = out.system;
80
+ events.push(...out.events);
81
+ // H7: every delivery and its events, in order, each entry keyed with the last one's key as `prev`.
82
+ const seq = log.length;
83
+ const prev = seq === 0 ? GENESIS : log[seq - 1].key;
84
+ const entry = { seq, prev, delivery, events: out.events };
85
+ log.push({ ...entry, key: k.key("logEntry", entry) });
86
+ for (const event of out.events) {
87
+ if (event.kind === "out" && event.performed && event.action.kind === "call") {
88
+ // H5: exactly the calls the interpreter performed, each once.
89
+ perform(event);
90
+ } else if (event.kind === "out" && event.performed && event.action.kind === "cancel") {
91
+ // H6: cancellation goes to the implementation, which still answers.
92
+ inFlight.get(key(event.origin, event.action.call))?.abort();
93
+ } else if (event.kind === "withheld" && fits(event.reason) && delivery.input.kind === "ret") {
94
+ // H8: a result that didn't fit leaves the call pending, so it fails instead.
95
+ const input = { kind: "failed", call: delivery.input.call, error: NO_FIT };
96
+ queue.unshift(delivery.to === undefined ? { input } : { to: delivery.to, input });
97
+ }
98
+ }
99
+ };
100
+
101
+ // H4: the start comes from the principal, with an honest label; results only from their implementations.
102
+ const start = { kind: "start", value: options.input ?? null };
103
+ if (options.label !== undefined) start.label = options.label;
104
+ step({ input: start });
105
+ for (;;) {
106
+ while (queue.length > 0) step(queue.shift());
107
+ if (inFlight.size === 0) break;
108
+ await new Promise((resolve) => {
109
+ wake = resolve;
110
+ });
111
+ }
112
+ return { outcome: system.root.outcome, system, events, log };
113
+ }
114
+
115
+ /** Whether a withheld result left its call pending: it was the call's, but didn't fit. */
116
+ const fits = (reason) => reason.kind === "mismatch" || reason.kind === "postcondition";
117
+
118
+ const key = (origin, call) => `${origin.join("/")}:${call}`;
119
+
120
+ function load(k, harness, capabilities, given) {
121
+ if (typeof harness !== "string") {
122
+ return { term: harness, iface: given ?? install(k, Object.keys(capabilities), capabilities) };
123
+ }
124
+ const out = k.elaborate(harness, capabilities);
125
+ if (out.kind !== "elaborated") throw new Error(`elaborate: ${JSON.stringify(out.problems)}`);
126
+ const iface =
127
+ given ??
128
+ install(
129
+ k,
130
+ out.uses.map((u) => [u.name, u.capability]),
131
+ capabilities,
132
+ );
133
+ return { term: out.harness, iface };
134
+ }
135
+
136
+ function install(k, uses, capabilities) {
137
+ const installed = k.install(
138
+ uses.map((u) => {
139
+ const [prefix, name] = Array.isArray(u) ? u : [u, u];
140
+ return { prefix, capability: capabilities[name] };
141
+ }),
142
+ );
143
+ if (installed.kind !== "installed") throw new Error(`install: ${JSON.stringify(installed)}`);
144
+ return installed.interface;
145
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.