openvibers/openvibe.contracts Commits

v0.34.1: tools.tool.run and tools.net.probe are active (the run API and jobs facade shipped in Tools 5ac8309); tools.run@1 lists the run API's further problem codes

OP OpenVibers committed 24 Sept 2026 · 0d1db230a7 · 9 files, +17 −9


Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex a3c69f3c..96090388 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,14 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
++## 0.34.1 — 2026-09-24
++
++- `tools.tool.run` and `tools.net.probe` are **active**: the run API (`POST /api/v1/tools/:id/run`) and the gateway
++ jobs facade shipped in OpenVibe.Tools 5ac8309 and are deployed. The SDK v0.6.0 tools client passes against them.
++- `tools.run@1` lists the problem codes the run API emits beyond 0.33.1's: tools.input.too_large (413),
++ tools.run.timeout (504), tools.origin.refused (403), tools.session_required (401), tools.run.file_not_found (404),
++ tools.run.failed (500). Additive; no schema change.
++
## 0.34.0 — 2026-09-24
Additive: `compat.js` reports no breaking change against v0.33.1. Closes the Contracts rows of the
ddiff --git a/contracts/tools/run.v1.json b/contracts/tools/run.v1.json
iindex cdde1a32..3ca31e39 100644
---- a/contracts/tools/run.v1.json
++++ b/contracts/tools/run.v1.json
@@@ -2,7 +2,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/tools/run.v1.json",
"title": "ToolsRun",
-- "description": "The answer of POST /api/v1/tools/{id}/run (tools.run-request@1; ADR-027). Finished: { state: succeeded, tool, result, took_ms } or { state: failed|cancelled, tool, error, took_ms }, with job set when the tool ran as a job. Not finished within wait_ms: { state: queued|running, tool, job, location }, sent with 202 and a Location header; follow the job there (GET, its events, its files; tools.job@1). result carries data (kind json, matching the descriptor's output schema), text (kind text) or files (kind file or files: the job's result files, downloaded from their url). took_ms runs from the request to the answer for an inline run, and from created_at to finished_at for a job. error is problem+json from the tool: tools.job.failed, tools.job.timeout, tools.job.cancelled, or a code the tool chose (tools.…), such as 413 tools.pdf.too_many_pages or 422 tools.pdf.wrong_password; its status is the problem's, not the HTTP answer's. A request refused before the tool ran is a problem+json answer, not a run.",
++ "description": "The answer of POST /api/v1/tools/{id}/run (tools.run-request@1; ADR-027). Finished: { state: succeeded, tool, result, took_ms } or { state: failed|cancelled, tool, error, took_ms }, with job set when the tool ran as a job. Not finished within wait_ms: { state: queued|running, tool, job, location }, sent with 202 and a Location header; follow the job there (GET, its events, its files; tools.job@1). result carries data (kind json, matching the descriptor's output schema), text (kind text) or files (kind file or files: the job's result files, downloaded from their url). took_ms runs from the request to the answer for an inline run, and from created_at to finished_at for a job. error is problem+json from the tool: tools.job.failed, tools.job.timeout, tools.job.cancelled, or a code the tool chose (tools.…), such as 413 tools.pdf.too_many_pages or 422 tools.pdf.wrong_password; its status is the problem's, not the HTTP answer's. A request refused before the tool ran is a problem+json answer, not a run. Also: 413 tools.input.too_large (the input exceeded a worker's memory or size limit), 504 tools.run.timeout (the run passed the descriptor's timeoutMs), 403 tools.origin.refused (a cookie-authenticated mutation from a foreign Origin), 401 tools.session_required (the tool is not anonymous and the call has no browser session or token), 404 tools.run.file_not_found (a files reference names no result the caller may read), 500 tools.run.failed (the engine failed without a more specific code).",
"oneOf": [
{
"type": "object", "required": ["state", "tool", "result", "took_ms"], "additionalProperties": false,
ddiff --git a/generated/json-schema/bundle.json b/generated/json-schema/bundle.json
iindex d53a0330..f1d28b75 100644
---- a/generated/json-schema/bundle.json
++++ b/generated/json-schema/bundle.json
@@@ -10780,7 +10780,7 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://openvibe.network/contracts/tools/run.v1.json",
"title": "ToolsRun",
-- "description": "The answer of POST /api/v1/tools/{id}/run (tools.run-request@1; ADR-027). Finished: { state: succeeded, tool, result, took_ms } or { state: failed|cancelled, tool, error, took_ms }, with job set when the tool ran as a job. Not finished within wait_ms: { state: queued|running, tool, job, location }, sent with 202 and a Location header; follow the job there (GET, its events, its files; tools.job@1). result carries data (kind json, matching the descriptor's output schema), text (kind text) or files (kind file or files: the job's result files, downloaded from their url). took_ms runs from the request to the answer for an inline run, and from created_at to finished_at for a job. error is problem+json from the tool: tools.job.failed, tools.job.timeout, tools.job.cancelled, or a code the tool chose (tools.…), such as 413 tools.pdf.too_many_pages or 422 tools.pdf.wrong_password; its status is the problem's, not the HTTP answer's. A request refused before the tool ran is a problem+json answer, not a run.",
++ "description": "The answer of POST /api/v1/tools/{id}/run (tools.run-request@1; ADR-027). Finished: { state: succeeded, tool, result, took_ms } or { state: failed|cancelled, tool, error, took_ms }, with job set when the tool ran as a job. Not finished within wait_ms: { state: queued|running, tool, job, location }, sent with 202 and a Location header; follow the job there (GET, its events, its files; tools.job@1). result carries data (kind json, matching the descriptor's output schema), text (kind text) or files (kind file or files: the job's result files, downloaded from their url). took_ms runs from the request to the answer for an inline run, and from created_at to finished_at for a job. error is problem+json from the tool: tools.job.failed, tools.job.timeout, tools.job.cancelled, or a code the tool chose (tools.…), such as 413 tools.pdf.too_many_pages or 422 tools.pdf.wrong_password; its status is the problem's, not the HTTP answer's. A request refused before the tool ran is a problem+json answer, not a run. Also: 413 tools.input.too_large (the input exceeded a worker's memory or size limit), 504 tools.run.timeout (the run passed the descriptor's timeoutMs), 403 tools.origin.refused (a cookie-authenticated mutation from a foreign Origin), 401 tools.session_required (the tool is not anonymous and the call has no browser session or token), 404 tools.run.file_not_found (a files reference names no result the caller may read), 500 tools.run.failed (the engine failed without a more specific code).",
"oneOf": [
{
"type": "object",
ddiff --git a/generated/typescript/index.d.ts b/generated/typescript/index.d.ts
iindex dfddc3e6..79d56c60 100644
---- a/generated/typescript/index.d.ts
++++ b/generated/typescript/index.d.ts
@@@ -9594,7 +9594,7 @@ export interface ToolsRunRequest {
/** [email protected] (owner: tools) */
/**
-- * The answer of POST /api/v1/tools/{id}/run (tools.run-request@1; ADR-027). Finished: { state: succeeded, tool, result, took_ms } or { state: failed|cancelled, tool, error, took_ms }, with job set when the tool ran as a job. Not finished within wait_ms: { state: queued|running, tool, job, location }, sent with 202 and a Location header; follow the job there (GET, its events, its files; tools.job@1). result carries data (kind json, matching the descriptor's output schema), text (kind text) or files (kind file or files: the job's result files, downloaded from their url). took_ms runs from the request to the answer for an inline run, and from created_at to finished_at for a job. error is problem+json from the tool: tools.job.failed, tools.job.timeout, tools.job.cancelled, or a code the tool chose (tools.…), such as 413 tools.pdf.too_many_pages or 422 tools.pdf.wrong_password; its status is the problem's, not the HTTP answer's. A request refused before the tool ran is a problem+json answer, not a run.
++ * The answer of POST /api/v1/tools/{id}/run (tools.run-request@1; ADR-027). Finished: { state: succeeded, tool, result, took_ms } or { state: failed|cancelled, tool, error, took_ms }, with job set when the tool ran as a job. Not finished within wait_ms: { state: queued|running, tool, job, location }, sent with 202 and a Location header; follow the job there (GET, its events, its files; tools.job@1). result carries data (kind json, matching the descriptor's output schema), text (kind text) or files (kind file or files: the job's result files, downloaded from their url). took_ms runs from the request to the answer for an inline run, and from created_at to finished_at for a job. error is problem+json from the tool: tools.job.failed, tools.job.timeout, tools.job.cancelled, or a code the tool chose (tools.…), such as 413 tools.pdf.too_many_pages or 422 tools.pdf.wrong_password; its status is the problem's, not the HTTP answer's. A request refused before the tool ran is a problem+json answer, not a run. Also: 413 tools.input.too_large (the input exceeded a worker's memory or size limit), 504 tools.run.timeout (the run passed the descriptor's timeoutMs), 403 tools.origin.refused (a cookie-authenticated mutation from a foreign Origin), 401 tools.session_required (the tool is not anonymous and the call has no browser session or token), 404 tools.run.file_not_found (a files reference names no result the caller may read), 500 tools.run.failed (the engine failed without a more specific code).
*/
export type ToolsRun =
| {
ddiff --git a/manifests/capabilities/tools.net.probe.json b/manifests/capabilities/tools.net.probe.json
iindex 569c5e8e..03948131 100644
---- a/manifests/capabilities/tools.net.probe.json
++++ b/manifests/capabilities/tools.net.probe.json
@@@ -2,7 +2,7 @@
"id": "tools.net.probe",
"version": "1.0",
"owner": "tools",
-- "status": "planned",
++ "status": "active",
"visibility": "partner",
"description": "Run the network probe tools through the run API (ADR-027): the tools whose descriptor names auth.capability tools.net.probe (port checks, ping, traceroute, mtr, latency, bulk header and TLS checks), which send traffic to a host the caller chose. Only app and service principals holding this capability run them through the API or the SDK: partner visibility, so Network grants it to a developer project only when staff add it to that project's allowance by hand, and first-party services hold it through their own grants. People keep using the probe pages, which have a per-target throttle. Every run is throttled per target across all callers (limits.perTargetPerMinute) and written to the pseudonymous abuse log. Planned until the gateway serves the route.",
"inputSchema": "tools.run-request@1",
ddiff --git a/manifests/capabilities/tools.tool.run.json b/manifests/capabilities/tools.tool.run.json
iindex 101f6128..03d493d3 100644
---- a/manifests/capabilities/tools.tool.run.json
++++ b/manifests/capabilities/tools.tool.run.json
@@@ -2,7 +2,7 @@
"id": "tools.tool.run",
"version": "1.0",
"owner": "tools",
-- "status": "planned",
++ "status": "active",
"visibility": "public",
"description": "Run a tool through the uniform run API (ADR-027): POST /api/v1/tools/:id/run with tools.run-request@1 (JSON, or multipart with uploaded files), answered inline (tools.run@1 succeeded or failed) or, for a job tool that has not finished within wait_ms, with its job (202 + Location). Every tool whose descriptor has api true and auth.capability tools.tool.run. Callers are tiered, anonymous < session < user < app/service: tools with auth.anonymous true take anonymous callers (keyed by IP, IPv6 by /64); the others need a browser session, a signed-in person or a token. An app or service token needs this capability; people and browser sessions do not. Quotas count each tool's cost within its quota class, per caller. A job tool's run creates a job owned by the caller, like tools.job.create. Planned until the gateway serves the route.",
"inputSchema": "tools.run-request@1",
ddiff --git a/package-lock.json b/package-lock.json
iindex d5674a4e..1d5aa056 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-contracts",
-- "version": "0.34.0",
++ "version": "0.34.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-contracts",
-- "version": "0.34.0",
++ "version": "0.34.1",
"license": "AGPL-3.0-only",
"dependencies": {
"ajv": "^8.17.1",
ddiff --git a/package.json b/package.json
iindex 08ba55c5..a6bffb68 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-contracts",
-- "version": "0.34.0",
++ "version": "0.34.1",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
ddiff --git a/test/run.js b/test/run.js
iindex 26b9df15..6fbc1a5a 100644
---- a/test/run.js
++++ b/test/run.js
@@@ -284,7 +284,7 @@ for (const id of ['events.event.publish', 'events.event.read', 'events.subscript
ok(D({ ...byId.jsonminify, input: { ...byId.jsonminify.input, $id: 'https://openvibe.tools/api/v1/tools/jsonminify/schema' }, examples: [{ input: { text: '{}' } }] }) && D({ ...byId.port, input: { ...byId.port.input, $id: 'https://openvibe.tools/api/v1/tools/jsonminify/schema' }, examples: [{ input: { host: 'a.example', ports: [1] } }] }), 'input schemas that carry the same $id do not clash');
// v0.33.1: the registry is served (tools.tool.read active); the run API and probes are not yet.
-- ok(readCap.status === 'active' && runCap.status === 'planned' && probeCap.status === 'planned', 'tools.tool.read is active; tools.tool.run and tools.net.probe stay planned until the run API ships');
++ ok(readCap.status === 'active' && runCap.status === 'active' && probeCap.status === 'active', 'tools.tool.read, tools.tool.run and tools.net.probe are active (the run API shipped in Tools 5ac8309, 2026-09-24)');
const statuses = contracts.schema('tools.tool').properties.status.enum;
ok(!statuses.includes('planned') && /no planned status/.test(contracts.schema('tools.tool').description) && /planned placeholders are not tools and are not listed/.test(contracts.schema('tools.tool-list').description), 'there is no planned status: planned entries are not tools, and the contracts say so');