Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions runtime/tests/runtime-signing.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,37 @@ test("POST /execute dispatches execution.verb to clean and keeps canonical commo
}
});

test("POST /execute normalizes nested execution into the handler body and preserves execution metadata", async () => {
const keys = makeKeys();
const srv = await startServer({
RECEIPT_SIGNING_PRIVATE_KEY_PEM_B64: keys.privatePemB64,
RECEIPT_SIGNING_PUBLIC_KEY_B64: keys.publicRaw32B64,
RECEIPT_SIGNER_ID: "runtime.commandlayer.eth",
});

try {
const resp = await fetch(`${srv.base}/execute`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
execution: { verb: "describe", version: "1.1.0", class: "commons" },
input: { subject: "Normalize me" },
}),
});
const json = await resp.json();
const { receipt } = unwrapReceiptResponse(json);

assert.equal(resp.status, 200);
assert.equal(receipt.verb, "describe");
assert.equal(receipt.version, "1.1.0");
assert.equal(receipt.class, "commons");
assert.equal(typeof receipt.result.description, "string");
assert.ok(receipt.result.description.length > 0);
} finally {
await stop(srv.proc);
}
});

test("POST /execute falls back to top-level verb for summarize", async () => {
const keys = makeKeys();
const srv = await startServer({
Expand Down
16 changes: 13 additions & 3 deletions server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1540,7 +1540,7 @@ async function handleVerb(verb, req, res) {
};

try {
const execution = normalizeExecutionEnvelope(req.body?.execution, verb);
const execution = normalizeExecutionEnvelope(req.body?.execution ?? req.body, verb);
warmValidatorForVerb(execution.verb);

const callerTimeout = Number(req.body?.limits?.timeout_ms || req.body?.limits?.max_latency_ms || 0);
Expand All @@ -1566,7 +1566,7 @@ async function handleVerb(verb, req, res) {
}

} catch (e) {
const execution = normalizeExecutionEnvelope(req.body?.execution, verb);
const execution = normalizeExecutionEnvelope(req.body?.execution ?? req.body, verb);
warmValidatorForVerb(execution.verb);

const actor = req.body?.actor
Expand Down Expand Up @@ -2025,10 +2025,20 @@ app.post("/verify", async (req, res) => {
// verb routes
// -----------------------
app.post("/execute", (req, res) => {
const resolvedVerb = String(req.body?.execution?.verb || req.body?.verb || "").trim();
const body = req.body && typeof req.body === "object" ? req.body : {};
const execution = body.execution && typeof body.execution === "object" ? body.execution : body;
const resolvedVerb = String(execution.verb || body.verb || "").trim();

if (!resolvedVerb) {
return res.status(400).json({ ok: false, error: "missing_verb", message: "execution.verb or verb is required", ...instancePayload() });
}

req.body = {
...body,
...execution,
verb: resolvedVerb,
};

return handleVerb(resolvedVerb, req, res);
});

Expand Down
Loading