openvibers/openvibe.codes Commits
A runtime capability manifest on every harness offer (#369)
Changes
@@@ -82,7 +82,10 @@ first `docs/adr/` — the plan (§8) lists **ADR-050 Codes Harness Fabric** amon(`app.js:79-80`), never a runtime surprise.3. **One harness × agent pair is one `platform.resource-offer@1` of `kind: harness`** with the catalog row as its`platform.harness-offer@1` `detail` (`harness-placement.js:31-58`). Offers carry `task:<kind>`, `harness:<flag>` and-- `runtime:<class>` capabilities; a row that declares no runtime declares none (no default is synthesized).++ `runtime:<class>` capabilities; a row that declares no runtime declares none (no default is synthesized). They also++ carry one `harness:<field>-<value>` capability per field of the runtime capability manifest++ (`engine/runtime.js`, `docs/harness-runtime.md`) — terminal status, steering, checkpointing, subagents, context++ handoff and enforcement — so the router routes on the harness's declared behavior, never on a provider name.4. **The read surface is `GET /api/v1/harnesses` and `POST /api/v1/harnesses/route`, plus the read-only docs page.**These are additive and public-read; they expose the catalog and the placement decision with reasons and candidates(`api.js:61-81`). Nothing in this surface launches a harness, streams a run, meters usage or resumes a session.nnew file mode 100644@@@ -0,0 +1,68 @@++# The runtime capability manifest++++Every harness in `server/data/harness-offers.json` declares what it can actually do at runtime, in++`engine/runtime.js`. The manifest is data, not a provider-name check: a task states the capability it needs and the++router excludes every harness that does not declare it, with a reason. This is the plan's "runtime capability++manifest on every harness offer" (Release 1, feat 12; t3code's provider capability system is the model).++++The catalog row cannot carry it: `platform.harness-offer@1` closes the row to unknown fields. The contract's own place++for a runtime capability is the offer's `capabilities` array, so `toOffer()` (`server/domain/harness-placement.js`)++turns each field into one `harness:<field>-<value>` capability. A harness the manifest does not know declares++nothing, and is excluded only when a requirement is set.++++## Fields and values++++| field | values | meaning |++|---|---|---|++| `terminal_status` | `structured` | a machine-readable result line (stream-json, or the API loop's own events) |++| | `parsed` | Codes reads the CLI's prose and decides from it |++| | `none` | nothing to read |++| `steering` | `live` | a message can be appended to the running session |++| | `resume` | the session is continued by id, after or between runs |++| | `none` | a new run starts over |++| `checkpointing` | `git` | Codes can write the worktree to a git ref and restore it |++| | `none` | no checkpoint |++| `subagents` | `native` | the harness's own children, with its own lifecycle and completion |++| | `tool` | children through a shell or protocol with no lifecycle report |++| | `none` | no children |++| `context_handoff` | `session` | the harness's session id resumes the conversation |++| | `transcript` | Codes' own store replays it |++| | `none` | only the hand-off note |++| `enforcement` | `native` | the harness's own sandbox or permission mode |++| | `client-boundary` | Codes' tool loop, or a sandbox Codes requires around it |++++## The rows++++| harness | terminal_status | steering | checkpointing | subagents | context_handoff | enforcement |++|---|---|---|---|---|---|---|++| `claude-code` | structured | resume | none | native | session | native |++| `claude-other` | structured | resume | none | native | session | native |++| `codex` | structured | resume | none | none | session | native |++| `opencode` | structured | resume | none | none | session | client-boundary |++| `command-code` | structured | resume | none | none | session | client-boundary |++| `aider` | parsed | none | none | none | none | client-boundary |++| `minimax-code` | structured | resume | none | none | session | native |++| `deepseek` | structured | resume | none | none | session | client-boundary |++| `openvibe-ai` | structured | resume | none | none | session | client-boundary |++| `openai-compatible` | structured | resume | none | none | session | client-boundary |++++`checkpointing` flips to `git` on every row when the per-turn checkpoint refs land (`harness/job.js`): the manifest is++the runtime's declaration, so it must not claim a checkpoint before one exists.++++## Using it++++```js++const runtime = require('./engine/runtime');++++runtime.forHarness('codex'); // the fields++runtime.capabilities(runtime.forHarness('codex')); // ['harness:terminal-status-structured', …]++runtime.fits('cm', { subagents: 'native' }); // true: cm is Claude Code++runtime.fits('mm', { subagents: 'native' }); // false: MiniMax Code declares none++```++++- `POST /api/v1/harnesses/route` and `openvibe-codes route --need` take the capability names directly:++ `{ "requirements": { "capabilities": ["harness:steering-resume"] } }`.++- `engine/router.js` `pick()` takes `needs` as the names or as `{ field: value }`, and skips a ladder rung whose++ harness does not declare it.++- `test/harness-runtime.test.js` keeps the manifest, the catalog and both routers in step; a harness added to the++ catalog without a manifest fails it.@@@ -52,6 +52,22 @@ shared Know store's own `recall()` (the portal's `know/lessons.js`; on a Link itEach CLI: installed, signed in, and the limit windows it can read. Nothing else: no key, no token, no account name.++### `runtime.js` — what each harness can actually do++++The runtime capability manifest: terminal-status quality, steering, checkpointing, subagent lifecycle, context++handoff and where the permission boundary is enforced, per harness, as data. Every offer carries the fields as++`harness:<field>-<value>` capabilities (`server/domain/harness-placement.js`), so a task requires what it needs and++the router excludes the rest with a reason instead of a provider-name check. `pick()` also takes `needs` and skips a++ladder rung whose harness does not declare it. `[docs/harness-runtime.md](../docs/harness-runtime.md)` has the table.++++```js++const runtime = require('./engine/runtime');++++runtime.fits('cm', { subagents: 'native' }); // true++runtime.fits('mm', { subagents: 'native' }); // false: MiniMax Code declares no subagents++runtime.capabilities(runtime.forHarness('codex'));++```++### `router.js` — what kind of task is this, and which runner may take itA brief is classified into one of five tiers (`basic`, `research`, `standard`, `hard`, `design`); a line `tier: hard`@@@ -19,6 +19,7 @@const fs = require('fs');const path = require('path');const { fitEffort, stepUp, EFFORTS } = require('./effort');++const runtime = require('./runtime');const LADDER = {// Chores: renames, docs, formatting, counting, simple checks — free and plan Flash models first.@@@ -242,13 +243,17 @@ function connected(runner, connections) {* @param {object} [ctx.ladder] the ladder to use (the portal's by default)* @param {object} [ctx.connections] the person's connections, per runner (Codes C3)* @param {object} [ctx.fleet] the fleet per runner: { [runner]: { signed_in, windows } } (Codes C9c)++ * @param {string[]|object} [ctx.needs] runtime capabilities the task needs (engine/runtime.js), as names++ * (`['harness:steering-resume']`) or fields (`{ steering: 'resume' }`); a runner whose harness does not++ * declare one is skipped, never chosen and then found wanting* @returns {{runner: string, model: string|null, effort: string|null, tier: string, brief: string|null, why: string}|null}*/--function pick(prompt, { cfg, health, counts, models, runs, JOBS, name, ladder, connections, fleet } = {}) {++function pick(prompt, { cfg, health, counts, models, runs, JOBS, name, ladder, connections, fleet, needs } = {}) {const c = classify(prompt, { JOBS, name });const rec = track(runs);const reliable = (r) => { const t = rec[r]; return !t || t.done + t.failed < 4 || t.done / (t.done + t.failed) >= 0.4; };-- const usable = (r) => cfg.subagents[r] && cfg.subagents[r].enabled && (counts[r] || 0) < cfg.subagents[r].max && health[r] && health[r].state === 'ok' && reliable(r) && connected(r, connections) && roomFor(fleet, r);++ const capable = (r) => runtime.fits(r, needs);++ const usable = (r) => cfg.subagents[r] && cfg.subagents[r].enabled && (counts[r] || 0) < cfg.subagents[r].max && health[r] && health[r].state === 'ok' && reliable(r) && capable(r) && connected(r, connections) && roomFor(fleet, r);for (const [runner, model, wanted] of (ladder || LADDER)[c.tier]) {if (!usable(runner)) continue;// A model the runner no longer lists is skipped for that runner's default (lists refresh from each provider).nnew file mode 100644@@@ -0,0 +1,180 @@++'use strict';++++/**++ * The runtime capability manifest: what each harness Codes runs can actually do, as data.++ *++ * Every offer carries these as `harness:<field>-<value>` capabilities (`server/domain/harness-placement.js`), so a++ * task states what it needs — "a harness that can resume a session", "one whose sandbox is its own" — and the router++ * excludes the rest with a reason. No caller asks a provider's name; a harness that lies here lies to the router.++ *++ * The fields (the plan's runtime cap, tools §4; t3code's provider capability system is the model):++ *++ * terminal_status how the harness reports how a run ended++ * structured — a machine-readable result line (stream-json / an API's own loop)++ * parsed — Codes reads the CLI's prose and decides from it++ * none — nothing to read++ * steering how a running or ended task takes a correction++ * live — a message can be appended to the running session++ * resume — the session is continued by id, after or between runs++ * none — a new run starts over++ * checkpointing whether Codes can checkpoint the harness's working tree per turn++ * git — the worktree can be written to a git ref and restored++ * none — no checkpoint++ * subagents how the harness runs children++ * native — the harness's own tools, with its own lifecycle and completion++ * tool — spawned through a shell or a protocol with no lifecycle report++ * none — no children++ * context_handoff what carries the context to the next attempt++ * session — the harness's session id resumes the conversation++ * transcript — Codes' own session store replays it++ * none — only the hand-off note++ * enforcement where the harness's permission boundary is enforced++ * native — the harness's own sandbox or permission mode++ * client-boundary — Codes' tool loop, or a sandbox Codes requires around it++ *++ * `none` is a declaration, not an omission: a requirement the harness cannot meet excludes it. An unknown harness (a++ * portal runner with no Codes catalog row) declares nothing, so it is excluded only when a requirement is set.++ *++ * The manifest is Codes' own data, not a field of `platform.harness-offer@1`: the contract closes the row to unknown++ * fields, and the offer's `capabilities` array is the contract's own place for a capability. A harness added to the++ * catalog without a manifest fails the test that keeps the two in step.++ */++++/** The closed value sets of every field. A value outside its set is a mistake, never a capability. */++const FIELDS = Object.freeze({++ terminal_status: Object.freeze(['structured', 'parsed', 'none']),++ steering: Object.freeze(['live', 'resume', 'none']),++ checkpointing: Object.freeze(['git', 'none']),++ subagents: Object.freeze(['native', 'tool', 'none']),++ context_handoff: Object.freeze(['session', 'transcript', 'none']),++ enforcement: Object.freeze(['native', 'client-boundary']),++});++++/** The capability namespace each field writes into: `terminal_status: 'structured'` → `harness:terminal-status-structured`. */++const CAPABILITY = Object.freeze({++ terminal_status: 'harness:terminal-status',++ steering: 'harness:steering',++ checkpointing: 'harness:checkpointing',++ subagents: 'harness:subagents',++ context_handoff: 'harness:context-handoff',++ enforcement: 'harness:enforcement',++});++++/**++ * One manifest per harness in `server/data/harness-offers.json`.++ *++ * Grounded in each adapter (harness/adapters/cli.js, harness/adapters/api.js) and its CLI's flags, not in its name:++ * every CLI here ends with a result line except Aider (its prose is parsed); every CLI with `--resume`/`--session`/`-s`++ * continues its session; Claude Code alone exposes subagents as tools. `checkpointing` is `none` until the per-turn++ * checkpoint refs land: this manifest is the runtime's declaration, so it must not claim one early.++ */++const MANIFEST = Object.freeze({++ 'claude-code': Object.freeze({++ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',++ subagents: 'native', context_handoff: 'session', enforcement: 'native',++ }),++ 'claude-other': Object.freeze({++ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',++ subagents: 'native', context_handoff: 'session', enforcement: 'native',++ }),++ codex: Object.freeze({++ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',++ subagents: 'none', context_handoff: 'session', enforcement: 'native',++ }),++ opencode: Object.freeze({++ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',++ subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',++ }),++ 'command-code': Object.freeze({++ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',++ subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',++ }),++ aider: Object.freeze({++ terminal_status: 'parsed', steering: 'none', checkpointing: 'none',++ subagents: 'none', context_handoff: 'none', enforcement: 'client-boundary',++ }),++ 'minimax-code': Object.freeze({++ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',++ subagents: 'none', context_handoff: 'session', enforcement: 'native',++ }),++ deepseek: Object.freeze({++ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',++ subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',++ }),++ 'openvibe-ai': Object.freeze({++ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',++ subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',++ }),++ 'openai-compatible': Object.freeze({++ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',++ subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',++ }),++});++++/**++ * The portal's short runner ids (engine/router.js's ladder) for the same harness. The portal has no row for++ * `claude-other`, and `cm`/`claude` are Claude Code on Anthropic; the others are one-to-one.++ */++const RUNNER_HARNESS = Object.freeze({++ cm: 'claude-code', claude: 'claude-code',++ cx: 'codex', cc: 'command-code', oc: 'opencode', dsh: 'deepseek', mm: 'minimax-code',++});++++/** The manifest for a harness id (Codes'), or null for one the catalog does not declare. */++function forHarness(harness) {++ return MANIFEST[harness] || null;++}++++/** The manifest for a ladder runner (the portal's short id, or a Codes harness id), or null. */++function forRunner(runner) {++ const key = String(runner || '');++ return MANIFEST[RUNNER_HARNESS[key] || key] || null;++}++++/** The offer capabilities one manifest declares, in field order. */++function capabilities(manifest) {++ if (!manifest) return [];++ return Object.keys(FIELDS).map((field) => `${CAPABILITY[field]}-${manifest[field]}`);++}++++/**++ * Turn a requirement into capability names. Two shapes are accepted:++ *++ * ['harness:steering-resume'] the capabilities as they are++ * { steering: 'resume', subagents: 'native' } the fields and their values++ *++ * @param {string[]|object|null} needs++ * @returns {string[]}++ */++function requirements(needs) {++ if (!needs) return [];++ if (Array.isArray(needs)) return needs.filter((c) => typeof c === 'string' && c.trim()).map((c) => c.trim());++ const caps = [];++ for (const [field, value] of Object.entries(needs)) {++ // An unknown field cannot be met: keep its name so the exclusion says why, rather than silently dropping it.++ caps.push(`${CAPABILITY[field] || `harness:${field.replace(/_/g, '-')}`}-${value}`);++ }++ return caps;++}++++/**++ * Does a harness or ladder runner declare every capability a caller needs? No requirement fits everyone; a harness the++ * manifest does not know fits nothing that is asked of it.++ *++ * @param {string} id a harness id or a portal runner id++ * @param {string[]|object|null} needs capability names or a { field: value } object++ * @returns {boolean}++ */++function fits(id, needs) {++ const wanted = requirements(needs);++ if (!wanted.length) return true;++ const manifest = forRunner(id);++ if (!manifest) return false;++ const has = new Set(capabilities(manifest));++ return wanted.every((cap) => has.has(cap));++}++++module.exports = {++ FIELDS, CAPABILITY, MANIFEST, RUNNER_HARNESS,++ forHarness, forRunner, capabilities, requirements, fits,++};@@@ -6,6 +6,7 @@*/const contracts = require('openvibe-contracts');const { place, optionKey } = require('openvibe-sdk/placement');++const runtime = require('../../engine/runtime');// A catalog row without task_capabilities derives its task kinds from the capability booleans:// edit needs capabilities.edit === true, review needs capabilities.review === true.@@@ -34,6 +35,8 @@ function toOffer(harness, agent, opts = {}) {const caps = row.capabilities || {};const limits = row.limits || {};const tasks = row.task_capabilities || tasksFor(caps);++ // What this harness can actually do at runtime (engine/runtime.js): the fidelity the router needs, as capabilities.++ const fidelity = runtime.capabilities(runtime.forHarness(harness.id));const maxContextTokens = (agent.context_limits && agent.context_limits.input_tokens) ?? limits.max_context_tokens;const health = typeof opts.health === 'string' ? { status: opts.health } : opts.health || { status: 'up' };const offer = {@@@ -42,7 +45,7 @@ function toOffer(harness, agent, opts = {}) {provider: agent.provider || harness.provider,region: 'global',trust: opts.trust || 'external',-- capabilities: [...tasks.map((task) => `task:${task}`), ...Object.keys(HARNESS_FLAGS).filter((name) => caps[name] === true).map((name) => HARNESS_FLAGS[name]), ...(caps.runtimes || []).map((runtime) => `runtime:${runtime}`)],++ capabilities: [...tasks.map((task) => `task:${task}`), ...Object.keys(HARNESS_FLAGS).filter((name) => caps[name] === true).map((name) => HARNESS_FLAGS[name]), ...(caps.runtimes || []).map((kind) => `runtime:${kind}`), ...fidelity],capacity: { workers: { harness: limits.max_concurrent_runs } },health,pricing: { model: 'per-operation', unit: 'token', marginal_usd_per_unit: agent.price_per_1k_tokens.fresh_usd / 1000 },nnew file mode 100644@@@ -0,0 +1,77 @@++'use strict';++/**++ * The runtime capability manifest (engine/runtime.js): every catalog harness declares what it can do, every offer++ * carries it as `harness:<field>-<value>` capabilities, and both routers (placement and the tier router) exclude a++ * harness that does not declare what the task needs — no provider-name conditional in either.++ */++const assert = require('assert');++const fs = require('fs');++const path = require('path');++const { createHarnesses } = require('../server/domain/harnesses');++const { toOffer, route } = require('../server/domain/harness-placement');++const runtime = require('../engine/runtime');++const { pick } = require('../engine/router');++const { check, done } = require('./helpers/boot');++++const catalogPath = path.join(__dirname, '..', 'server', 'data', 'harness-offers.json');++const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));++++(async () => {++ await check('every catalog harness declares every field, with a known value', () => {++ for (const row of rows) {++ const manifest = runtime.forHarness(row.id);++ assert.ok(manifest, `${row.id} has no runtime manifest`);++ assert.deepStrictEqual(Object.keys(manifest).sort(), Object.keys(runtime.FIELDS).sort(), row.id);++ for (const [field, values] of Object.entries(runtime.FIELDS)) {++ assert.ok(values.includes(manifest[field]), `${row.id}.${field}: ${manifest[field]} is not one of ${values.join('|')}`);++ }++ }++ assert.deepStrictEqual(Object.keys(runtime.MANIFEST).sort(), rows.map((row) => row.id).sort(), 'no manifest without a catalog row');++ });++++ await check('a manifest becomes one capability per field, and fits() reads it', () => {++ assert.deepStrictEqual(runtime.capabilities(runtime.forHarness('claude-code')), [++ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',++ 'harness:subagents-native', 'harness:context-handoff-session', 'harness:enforcement-native',++ ]);++ assert.deepStrictEqual(runtime.requirements({ steering: 'resume', subagents: 'native' }), ['harness:steering-resume', 'harness:subagents-native']);++ assert.strictEqual(runtime.fits('claude-code', { subagents: 'native' }), true);++ assert.strictEqual(runtime.fits('codex', { subagents: 'native' }), false);++ assert.strictEqual(runtime.fits('cx', ['harness:steering-resume']), true, 'the portal runner id names the same harness');++ assert.strictEqual(runtime.fits('pg', ['harness:steering-resume']), false, 'an unknown runner declares nothing');++ assert.strictEqual(runtime.fits('pg', []), true, 'nothing required, nothing to exclude');++ });++++ await check('every offer carries the manifest as capabilities the placement router can require', () => {++ const harnesses = createHarnesses({ catalogPath });++ for (const harness of harnesses.list()) {++ const offer = toOffer(harness, harnesses.agents(harness.id)[0]);++ for (const cap of runtime.capabilities(runtime.forHarness(harness.id))) {++ assert.ok(offer.capabilities.includes(cap), `${offer.offer_id} lacks ${cap}`);++ }++ }++ const r = route({ task: 'edit', extra: { capabilities: ['harness:subagents-native'] }, harnesses, now: Date.now() });++ assert.strictEqual(r.selected.split(':')[0], 'claude-code');++ for (const id of ['codex:codex-default', 'opencode:opencode-default', 'aider:aider-default', 'deepseek:deepseek-chat']) {++ assert.deepStrictEqual(r.candidates.find((c) => c.id === id), { id, included: false, reason: 'lacks harness:subagents-native' });++ }++ const none = route({ task: 'edit', extra: { capabilities: ['harness:checkpointing-git'] }, harnesses, now: Date.now() });++ assert.strictEqual(none.selected, null, 'no harness declares checkpointing yet');++ assert.ok(none.candidates.every((c) => c.reason === 'lacks harness:checkpointing-git'));++ });++++ await check('the tier router skips a ladder rung its manifest cannot meet', () => {++ const ctx = {++ cfg: { subagents: { mm: { enabled: true, max: 2 }, cm: { enabled: true, max: 2 } } },++ health: { mm: { state: 'ok' }, cm: { state: 'ok' } },++ counts: {}, models: {}, runs: [],++ ladder: { basic: [['mm', null, 'high'], ['cm', null, 'high']] },++ };++ assert.strictEqual(pick('Fix the parser', ctx).runner, 'mm', 'no requirement: the first rung stays');++ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: ['harness:subagents-native'] }).runner, 'cm');++ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: { steering: 'resume' } }).runner, 'mm');++ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: ['harness:subagents-native'], ladder: { basic: [['mm', null, 'high']] } }), null);++ });++++ done();++})().catch((e) => { console.error(e); process.exit(1); });@@@ -40,7 +40,9 @@ function priced(row, freshUsd) {provider: 'example',region: 'global',trust: 'partner',-- capabilities: ['task:edit', 'harness:host-access', 'harness:mcp', 'harness:resume', 'harness:edit', 'harness:tools', 'runtime:code'],++ capabilities: ['task:edit', 'harness:host-access', 'harness:mcp', 'harness:resume', 'harness:edit', 'harness:tools', 'runtime:code',++ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',++ 'harness:subagents-none', 'harness:context-handoff-session', 'harness:enforcement-client-boundary'],capacity: { workers: { harness: 2 } },health: { status: 'degraded' },pricing: { model: 'per-operation', unit: 'token', marginal_usd_per_unit: 0.003 / 1000 },@@@ -52,7 +54,9 @@ function priced(row, freshUsd) {const deepseek = rows.find((row) => row.id === 'deepseek');const offer = toOffer(deepseek, deepseek.agents[0]);assert.strictEqual(offer.offer_id, 'deepseek:deepseek-chat');-- assert.deepStrictEqual(offer.capabilities, ['task:edit', 'task:review', 'harness:host-access', 'harness:resume', 'harness:edit', 'harness:review', 'harness:tools']);++ assert.deepStrictEqual(offer.capabilities, ['task:edit', 'task:review', 'harness:host-access', 'harness:resume', 'harness:edit', 'harness:review', 'harness:tools',++ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',++ 'harness:subagents-none', 'harness:context-handoff-session', 'harness:enforcement-client-boundary']);assert.strictEqual(offer.trust, 'external');assert.deepStrictEqual(offer.health, { status: 'up' });assert.strictEqual('latency_ms' in offer, false);@@@ -73,7 +77,9 @@ function priced(row, freshUsd) {}const browsing = { ...rows[0], task_capabilities: ['edit', 'browse'] };const offer = toOffer(browsing, browsing.agents[0], { now });-- assert.deepStrictEqual(offer.capabilities, ['task:edit', 'task:browse', 'harness:host-access', 'harness:mcp', 'harness:long-autonomy', 'harness:resume', 'harness:edit', 'harness:review', 'harness:tools', 'harness:vision', 'runtime:code']);++ assert.deepStrictEqual(offer.capabilities, ['task:edit', 'task:browse', 'harness:host-access', 'harness:mcp', 'harness:long-autonomy', 'harness:resume', 'harness:edit', 'harness:review', 'harness:tools', 'harness:vision', 'runtime:code',++ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',++ 'harness:subagents-native', 'harness:context-handoff-session', 'harness:enforcement-native']);assert.ok(contracts.validate('platform.resource-offer@1', JSON.parse(JSON.stringify(offer))).valid);});nnew file mode 100644@@@ -0,0 +1,77 @@++'use strict';++/**++ * The runtime capability manifest (engine/runtime.js): every catalog harness declares what it can do, every offer++ * carries it as `harness:<field>-<value>` capabilities, and both routers (placement and the tier router) exclude a++ * harness that does not declare what the task needs — no provider-name conditional in either.++ */++const assert = require('assert');++const fs = require('fs');++const path = require('path');++const { createHarnesses } = require('../server/domain/harnesses');++const { toOffer, route } = require('../server/domain/harness-placement');++const runtime = require('../engine/runtime');++const { pick } = require('../engine/router');++const { check, done } = require('./helpers/boot');++++const catalogPath = path.join(__dirname, '..', 'server', 'data', 'harness-offers.json');++const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));++++(async () => {++ await check('every catalog harness declares every field, with a known value', () => {++ for (const row of rows) {++ const manifest = runtime.forHarness(row.id);++ assert.ok(manifest, `${row.id} has no runtime manifest`);++ assert.deepStrictEqual(Object.keys(manifest).sort(), Object.keys(runtime.FIELDS).sort(), row.id);++ for (const [field, values] of Object.entries(runtime.FIELDS)) {++ assert.ok(values.includes(manifest[field]), `${row.id}.${field}: ${manifest[field]} is not one of ${values.join('|')}`);++ }++ }++ assert.deepStrictEqual(Object.keys(runtime.MANIFEST).sort(), rows.map((row) => row.id).sort(), 'no manifest without a catalog row');++ });++++ await check('a manifest becomes one capability per field, and fits() reads it', () => {++ assert.deepStrictEqual(runtime.capabilities(runtime.forHarness('claude-code')), [++ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',++ 'harness:subagents-native', 'harness:context-handoff-session', 'harness:enforcement-native',++ ]);++ assert.deepStrictEqual(runtime.requirements({ steering: 'resume', subagents: 'native' }), ['harness:steering-resume', 'harness:subagents-native']);++ assert.strictEqual(runtime.fits('claude-code', { subagents: 'native' }), true);++ assert.strictEqual(runtime.fits('codex', { subagents: 'native' }), false);++ assert.strictEqual(runtime.fits('cx', ['harness:steering-resume']), true, 'the portal runner id names the same harness');++ assert.strictEqual(runtime.fits('pg', ['harness:steering-resume']), false, 'an unknown runner declares nothing');++ assert.strictEqual(runtime.fits('pg', []), true, 'nothing required, nothing to exclude');++ });++++ await check('every offer carries the manifest as capabilities the placement router can require', () => {++ const harnesses = createHarnesses({ catalogPath });++ for (const harness of harnesses.list()) {++ const offer = toOffer(harness, harnesses.agents(harness.id)[0]);++ for (const cap of runtime.capabilities(runtime.forHarness(harness.id))) {++ assert.ok(offer.capabilities.includes(cap), `${offer.offer_id} lacks ${cap}`);++ }++ }++ const r = route({ task: 'edit', extra: { capabilities: ['harness:subagents-native'] }, harnesses, now: Date.now() });++ assert.strictEqual(r.selected.split(':')[0], 'claude-code');++ for (const id of ['codex:codex-default', 'opencode:opencode-default', 'aider:aider-default', 'deepseek:deepseek-chat']) {++ assert.deepStrictEqual(r.candidates.find((c) => c.id === id), { id, included: false, reason: 'lacks harness:subagents-native' });++ }++ const none = route({ task: 'edit', extra: { capabilities: ['harness:checkpointing-git'] }, harnesses, now: Date.now() });++ assert.strictEqual(none.selected, null, 'no harness declares checkpointing yet');++ assert.ok(none.candidates.every((c) => c.reason === 'lacks harness:checkpointing-git'));++ });++++ await check('the tier router skips a ladder rung its manifest cannot meet', () => {++ const ctx = {++ cfg: { subagents: { mm: { enabled: true, max: 2 }, cm: { enabled: true, max: 2 } } },++ health: { mm: { state: 'ok' }, cm: { state: 'ok' } },++ counts: {}, models: {}, runs: [],++ ladder: { basic: [['mm', null, 'high'], ['cm', null, 'high']] },++ };++ assert.strictEqual(pick('Fix the parser', ctx).runner, 'mm', 'no requirement: the first rung stays');++ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: ['harness:subagents-native'] }).runner, 'cm');++ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: { steering: 'resume' } }).runner, 'mm');++ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: ['harness:subagents-native'], ladder: { basic: [['mm', null, 'high']] } }), null);++ });++++ done();++})().catch((e) => { console.error(e); process.exit(1); });
@@ -82,7 +82,10 @@ first `docs/adr/` — the plan (§8) lists **ADR-050 Codes Harness Fabric** amon
@@ -82,7 +82,10 @@ first `docs/adr/` — the plan (§8) lists **ADR-050 Codes Harness Fabric** amon
(`app.js:79-80`), never a runtime surprise.
(`app.js:79-80`), never a runtime surprise.
3. **One harness × agent pair is one `platform.resource-offer@1` of `kind: harness`** with the catalog row as its
3. **One harness × agent pair is one `platform.resource-offer@1` of `kind: harness`** with the catalog row as its
`platform.harness-offer@1` `detail` (`harness-placement.js:31-58`). Offers carry `task:<kind>`, `harness:<flag>` and
`platform.harness-offer@1` `detail` (`harness-placement.js:31-58`). Offers carry `task:<kind>`, `harness:<flag>` and
- `runtime:<class>` capabilities; a row that declares no runtime declares none (no default is synthesized).
+ `runtime:<class>` capabilities; a row that declares no runtime declares none (no default is synthesized). They also
+ carry one `harness:<field>-<value>` capability per field of the runtime capability manifest
+ (`engine/runtime.js`, `docs/harness-runtime.md`) — terminal status, steering, checkpointing, subagents, context
+ handoff and enforcement — so the router routes on the harness's declared behavior, never on a provider name.
4. **The read surface is `GET /api/v1/harnesses` and `POST /api/v1/harnesses/route`, plus the read-only docs page.**
4. **The read surface is `GET /api/v1/harnesses` and `POST /api/v1/harnesses/route`, plus the read-only docs page.**
These are additive and public-read; they expose the catalog and the placement decision with reasons and candidates
These are additive and public-read; they expose the catalog and the placement decision with reasons and candidates
(`api.js:61-81`). Nothing in this surface launches a harness, streams a run, meters usage or resumes a session.
(`api.js:61-81`). Nothing in this surface launches a harness, streams a run, meters usage or resumes a session.
new file mode 100644
new file mode 100644
@@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
+# The runtime capability manifest
+
+Every harness in `server/data/harness-offers.json` declares what it can actually do at runtime, in
+`engine/runtime.js`. The manifest is data, not a provider-name check: a task states the capability it needs and the
+router excludes every harness that does not declare it, with a reason. This is the plan's "runtime capability
+manifest on every harness offer" (Release 1, feat 12; t3code's provider capability system is the model).
+
+The catalog row cannot carry it: `platform.harness-offer@1` closes the row to unknown fields. The contract's own place
+for a runtime capability is the offer's `capabilities` array, so `toOffer()` (`server/domain/harness-placement.js`)
+turns each field into one `harness:<field>-<value>` capability. A harness the manifest does not know declares
+nothing, and is excluded only when a requirement is set.
+
+## Fields and values
+
+| field | values | meaning |
+|---|---|---|
+| `terminal_status` | `structured` | a machine-readable result line (stream-json, or the API loop's own events) |
+| | `parsed` | Codes reads the CLI's prose and decides from it |
+| | `none` | nothing to read |
+| `steering` | `live` | a message can be appended to the running session |
+| | `resume` | the session is continued by id, after or between runs |
+| | `none` | a new run starts over |
+| `checkpointing` | `git` | Codes can write the worktree to a git ref and restore it |
+| | `none` | no checkpoint |
+| `subagents` | `native` | the harness's own children, with its own lifecycle and completion |
+| | `tool` | children through a shell or protocol with no lifecycle report |
+| | `none` | no children |
+| `context_handoff` | `session` | the harness's session id resumes the conversation |
+| | `transcript` | Codes' own store replays it |
+| | `none` | only the hand-off note |
+| `enforcement` | `native` | the harness's own sandbox or permission mode |
+| | `client-boundary` | Codes' tool loop, or a sandbox Codes requires around it |
+
+## The rows
+
+| harness | terminal_status | steering | checkpointing | subagents | context_handoff | enforcement |
+|---|---|---|---|---|---|---|
+| `claude-code` | structured | resume | none | native | session | native |
+| `claude-other` | structured | resume | none | native | session | native |
+| `codex` | structured | resume | none | none | session | native |
+| `opencode` | structured | resume | none | none | session | client-boundary |
+| `command-code` | structured | resume | none | none | session | client-boundary |
+| `aider` | parsed | none | none | none | none | client-boundary |
+| `minimax-code` | structured | resume | none | none | session | native |
+| `deepseek` | structured | resume | none | none | session | client-boundary |
+| `openvibe-ai` | structured | resume | none | none | session | client-boundary |
+| `openai-compatible` | structured | resume | none | none | session | client-boundary |
+
+`checkpointing` flips to `git` on every row when the per-turn checkpoint refs land (`harness/job.js`): the manifest is
+the runtime's declaration, so it must not claim a checkpoint before one exists.
+
+## Using it
+
+```js
+const runtime = require('./engine/runtime');
+
+runtime.forHarness('codex'); // the fields
+runtime.capabilities(runtime.forHarness('codex')); // ['harness:terminal-status-structured', …]
+runtime.fits('cm', { subagents: 'native' }); // true: cm is Claude Code
+runtime.fits('mm', { subagents: 'native' }); // false: MiniMax Code declares none
+```
+
+- `POST /api/v1/harnesses/route` and `openvibe-codes route --need` take the capability names directly:
+ `{ "requirements": { "capabilities": ["harness:steering-resume"] } }`.
+- `engine/router.js` `pick()` takes `needs` as the names or as `{ field: value }`, and skips a ladder rung whose
+ harness does not declare it.
+- `test/harness-runtime.test.js` keeps the manifest, the catalog and both routers in step; a harness added to the
+ catalog without a manifest fails it.
@@ -52,6 +52,22 @@ shared Know store's own `recall()` (the portal's `know/lessons.js`; on a Link it
@@ -52,6 +52,22 @@ shared Know store's own `recall()` (the portal's `know/lessons.js`; on a Link it
Each CLI: installed, signed in, and the limit windows it can read. Nothing else: no key, no token, no account name.
Each CLI: installed, signed in, and the limit windows it can read. Nothing else: no key, no token, no account name.
+### `runtime.js` — what each harness can actually do
+
+The runtime capability manifest: terminal-status quality, steering, checkpointing, subagent lifecycle, context
+handoff and where the permission boundary is enforced, per harness, as data. Every offer carries the fields as
+`harness:<field>-<value>` capabilities (`server/domain/harness-placement.js`), so a task requires what it needs and
+the router excludes the rest with a reason instead of a provider-name check. `pick()` also takes `needs` and skips a
+ladder rung whose harness does not declare it. `[docs/harness-runtime.md](../docs/harness-runtime.md)` has the table.
+
+```js
+const runtime = require('./engine/runtime');
+
+runtime.fits('cm', { subagents: 'native' }); // true
+runtime.fits('mm', { subagents: 'native' }); // false: MiniMax Code declares no subagents
+runtime.capabilities(runtime.forHarness('codex'));
+```
+
### `router.js` — what kind of task is this, and which runner may take it
### `router.js` — what kind of task is this, and which runner may take it
A brief is classified into one of five tiers (`basic`, `research`, `standard`, `hard`, `design`); a line `tier: hard`
A brief is classified into one of five tiers (`basic`, `research`, `standard`, `hard`, `design`); a line `tier: hard`
@@ -19,6 +19,7 @@
@@ -19,6 +19,7 @@
const fs = require('fs');
const fs = require('fs');
const path = require('path');
const path = require('path');
const { fitEffort, stepUp, EFFORTS } = require('./effort');
const { fitEffort, stepUp, EFFORTS } = require('./effort');
+const runtime = require('./runtime');
const LADDER = {
const LADDER = {
// Chores: renames, docs, formatting, counting, simple checks — free and plan Flash models first.
// Chores: renames, docs, formatting, counting, simple checks — free and plan Flash models first.
@@ -242,13 +243,17 @@ function connected(runner, connections) {
@@ -242,13 +243,17 @@ function connected(runner, connections) {
* @param {object} [ctx.ladder] the ladder to use (the portal's by default)
* @param {object} [ctx.ladder] the ladder to use (the portal's by default)
* @param {object} [ctx.connections] the person's connections, per runner (Codes C3)
* @param {object} [ctx.connections] the person's connections, per runner (Codes C3)
* @param {object} [ctx.fleet] the fleet per runner: { [runner]: { signed_in, windows } } (Codes C9c)
* @param {object} [ctx.fleet] the fleet per runner: { [runner]: { signed_in, windows } } (Codes C9c)
+ * @param {string[]|object} [ctx.needs] runtime capabilities the task needs (engine/runtime.js), as names
+ * (`['harness:steering-resume']`) or fields (`{ steering: 'resume' }`); a runner whose harness does not
+ * declare one is skipped, never chosen and then found wanting
* @returns {{runner: string, model: string|null, effort: string|null, tier: string, brief: string|null, why: string}|null}
* @returns {{runner: string, model: string|null, effort: string|null, tier: string, brief: string|null, why: string}|null}
*/
*/
-function pick(prompt, { cfg, health, counts, models, runs, JOBS, name, ladder, connections, fleet } = {}) {
+function pick(prompt, { cfg, health, counts, models, runs, JOBS, name, ladder, connections, fleet, needs } = {}) {
const c = classify(prompt, { JOBS, name });
const c = classify(prompt, { JOBS, name });
const rec = track(runs);
const rec = track(runs);
const reliable = (r) => { const t = rec[r]; return !t || t.done + t.failed < 4 || t.done / (t.done + t.failed) >= 0.4; };
const reliable = (r) => { const t = rec[r]; return !t || t.done + t.failed < 4 || t.done / (t.done + t.failed) >= 0.4; };
- const usable = (r) => cfg.subagents[r] && cfg.subagents[r].enabled && (counts[r] || 0) < cfg.subagents[r].max && health[r] && health[r].state === 'ok' && reliable(r) && connected(r, connections) && roomFor(fleet, r);
+ const capable = (r) => runtime.fits(r, needs);
+ const usable = (r) => cfg.subagents[r] && cfg.subagents[r].enabled && (counts[r] || 0) < cfg.subagents[r].max && health[r] && health[r].state === 'ok' && reliable(r) && capable(r) && connected(r, connections) && roomFor(fleet, r);
for (const [runner, model, wanted] of (ladder || LADDER)[c.tier]) {
for (const [runner, model, wanted] of (ladder || LADDER)[c.tier]) {
if (!usable(runner)) continue;
if (!usable(runner)) continue;
// A model the runner no longer lists is skipped for that runner's default (lists refresh from each provider).
// A model the runner no longer lists is skipped for that runner's default (lists refresh from each provider).
new file mode 100644
new file mode 100644
@@ -0,0 +1,180 @@
@@ -0,0 +1,180 @@
+'use strict';
+
+/**
+ * The runtime capability manifest: what each harness Codes runs can actually do, as data.
+ *
+ * Every offer carries these as `harness:<field>-<value>` capabilities (`server/domain/harness-placement.js`), so a
+ * task states what it needs — "a harness that can resume a session", "one whose sandbox is its own" — and the router
+ * excludes the rest with a reason. No caller asks a provider's name; a harness that lies here lies to the router.
+ *
+ * The fields (the plan's runtime cap, tools §4; t3code's provider capability system is the model):
+ *
+ * terminal_status how the harness reports how a run ended
+ * structured — a machine-readable result line (stream-json / an API's own loop)
+ * parsed — Codes reads the CLI's prose and decides from it
+ * none — nothing to read
+ * steering how a running or ended task takes a correction
+ * live — a message can be appended to the running session
+ * resume — the session is continued by id, after or between runs
+ * none — a new run starts over
+ * checkpointing whether Codes can checkpoint the harness's working tree per turn
+ * git — the worktree can be written to a git ref and restored
+ * none — no checkpoint
+ * subagents how the harness runs children
+ * native — the harness's own tools, with its own lifecycle and completion
+ * tool — spawned through a shell or a protocol with no lifecycle report
+ * none — no children
+ * context_handoff what carries the context to the next attempt
+ * session — the harness's session id resumes the conversation
+ * transcript — Codes' own session store replays it
+ * none — only the hand-off note
+ * enforcement where the harness's permission boundary is enforced
+ * native — the harness's own sandbox or permission mode
+ * client-boundary — Codes' tool loop, or a sandbox Codes requires around it
+ *
+ * `none` is a declaration, not an omission: a requirement the harness cannot meet excludes it. An unknown harness (a
+ * portal runner with no Codes catalog row) declares nothing, so it is excluded only when a requirement is set.
+ *
+ * The manifest is Codes' own data, not a field of `platform.harness-offer@1`: the contract closes the row to unknown
+ * fields, and the offer's `capabilities` array is the contract's own place for a capability. A harness added to the
+ * catalog without a manifest fails the test that keeps the two in step.
+ */
+
+/** The closed value sets of every field. A value outside its set is a mistake, never a capability. */
+const FIELDS = Object.freeze({
+ terminal_status: Object.freeze(['structured', 'parsed', 'none']),
+ steering: Object.freeze(['live', 'resume', 'none']),
+ checkpointing: Object.freeze(['git', 'none']),
+ subagents: Object.freeze(['native', 'tool', 'none']),
+ context_handoff: Object.freeze(['session', 'transcript', 'none']),
+ enforcement: Object.freeze(['native', 'client-boundary']),
+});
+
+/** The capability namespace each field writes into: `terminal_status: 'structured'` → `harness:terminal-status-structured`. */
+const CAPABILITY = Object.freeze({
+ terminal_status: 'harness:terminal-status',
+ steering: 'harness:steering',
+ checkpointing: 'harness:checkpointing',
+ subagents: 'harness:subagents',
+ context_handoff: 'harness:context-handoff',
+ enforcement: 'harness:enforcement',
+});
+
+/**
+ * One manifest per harness in `server/data/harness-offers.json`.
+ *
+ * Grounded in each adapter (harness/adapters/cli.js, harness/adapters/api.js) and its CLI's flags, not in its name:
+ * every CLI here ends with a result line except Aider (its prose is parsed); every CLI with `--resume`/`--session`/`-s`
+ * continues its session; Claude Code alone exposes subagents as tools. `checkpointing` is `none` until the per-turn
+ * checkpoint refs land: this manifest is the runtime's declaration, so it must not claim one early.
+ */
+const MANIFEST = Object.freeze({
+ 'claude-code': Object.freeze({
+ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
+ subagents: 'native', context_handoff: 'session', enforcement: 'native',
+ }),
+ 'claude-other': Object.freeze({
+ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
+ subagents: 'native', context_handoff: 'session', enforcement: 'native',
+ }),
+ codex: Object.freeze({
+ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
+ subagents: 'none', context_handoff: 'session', enforcement: 'native',
+ }),
+ opencode: Object.freeze({
+ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
+ subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',
+ }),
+ 'command-code': Object.freeze({
+ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
+ subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',
+ }),
+ aider: Object.freeze({
+ terminal_status: 'parsed', steering: 'none', checkpointing: 'none',
+ subagents: 'none', context_handoff: 'none', enforcement: 'client-boundary',
+ }),
+ 'minimax-code': Object.freeze({
+ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
+ subagents: 'none', context_handoff: 'session', enforcement: 'native',
+ }),
+ deepseek: Object.freeze({
+ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
+ subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',
+ }),
+ 'openvibe-ai': Object.freeze({
+ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
+ subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',
+ }),
+ 'openai-compatible': Object.freeze({
+ terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
+ subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',
+ }),
+});
+
+/**
+ * The portal's short runner ids (engine/router.js's ladder) for the same harness. The portal has no row for
+ * `claude-other`, and `cm`/`claude` are Claude Code on Anthropic; the others are one-to-one.
+ */
+const RUNNER_HARNESS = Object.freeze({
+ cm: 'claude-code', claude: 'claude-code',
+ cx: 'codex', cc: 'command-code', oc: 'opencode', dsh: 'deepseek', mm: 'minimax-code',
+});
+
+/** The manifest for a harness id (Codes'), or null for one the catalog does not declare. */
+function forHarness(harness) {
+ return MANIFEST[harness] || null;
+}
+
+/** The manifest for a ladder runner (the portal's short id, or a Codes harness id), or null. */
+function forRunner(runner) {
+ const key = String(runner || '');
+ return MANIFEST[RUNNER_HARNESS[key] || key] || null;
+}
+
+/** The offer capabilities one manifest declares, in field order. */
+function capabilities(manifest) {
+ if (!manifest) return [];
+ return Object.keys(FIELDS).map((field) => `${CAPABILITY[field]}-${manifest[field]}`);
+}
+
+/**
+ * Turn a requirement into capability names. Two shapes are accepted:
+ *
+ * ['harness:steering-resume'] the capabilities as they are
+ * { steering: 'resume', subagents: 'native' } the fields and their values
+ *
+ * @param {string[]|object|null} needs
+ * @returns {string[]}
+ */
+function requirements(needs) {
+ if (!needs) return [];
+ if (Array.isArray(needs)) return needs.filter((c) => typeof c === 'string' && c.trim()).map((c) => c.trim());
+ const caps = [];
+ for (const [field, value] of Object.entries(needs)) {
+ // An unknown field cannot be met: keep its name so the exclusion says why, rather than silently dropping it.
+ caps.push(`${CAPABILITY[field] || `harness:${field.replace(/_/g, '-')}`}-${value}`);
+ }
+ return caps;
+}
+
+/**
+ * Does a harness or ladder runner declare every capability a caller needs? No requirement fits everyone; a harness the
+ * manifest does not know fits nothing that is asked of it.
+ *
+ * @param {string} id a harness id or a portal runner id
+ * @param {string[]|object|null} needs capability names or a { field: value } object
+ * @returns {boolean}
+ */
+function fits(id, needs) {
+ const wanted = requirements(needs);
+ if (!wanted.length) return true;
+ const manifest = forRunner(id);
+ if (!manifest) return false;
+ const has = new Set(capabilities(manifest));
+ return wanted.every((cap) => has.has(cap));
+}
+
+module.exports = {
+ FIELDS, CAPABILITY, MANIFEST, RUNNER_HARNESS,
+ forHarness, forRunner, capabilities, requirements, fits,
+};
@@ -6,6 +6,7 @@
@@ -6,6 +6,7 @@
*/
*/
const contracts = require('openvibe-contracts');
const contracts = require('openvibe-contracts');
const { place, optionKey } = require('openvibe-sdk/placement');
const { place, optionKey } = require('openvibe-sdk/placement');
+const runtime = require('../../engine/runtime');
// A catalog row without task_capabilities derives its task kinds from the capability booleans:
// A catalog row without task_capabilities derives its task kinds from the capability booleans:
// edit needs capabilities.edit === true, review needs capabilities.review === true.
// edit needs capabilities.edit === true, review needs capabilities.review === true.
@@ -34,6 +35,8 @@ function toOffer(harness, agent, opts = {}) {
@@ -34,6 +35,8 @@ function toOffer(harness, agent, opts = {}) {
const caps = row.capabilities || {};
const caps = row.capabilities || {};
const limits = row.limits || {};
const limits = row.limits || {};
const tasks = row.task_capabilities || tasksFor(caps);
const tasks = row.task_capabilities || tasksFor(caps);
+ // What this harness can actually do at runtime (engine/runtime.js): the fidelity the router needs, as capabilities.
+ const fidelity = runtime.capabilities(runtime.forHarness(harness.id));
const maxContextTokens = (agent.context_limits && agent.context_limits.input_tokens) ?? limits.max_context_tokens;
const maxContextTokens = (agent.context_limits && agent.context_limits.input_tokens) ?? limits.max_context_tokens;
const health = typeof opts.health === 'string' ? { status: opts.health } : opts.health || { status: 'up' };
const health = typeof opts.health === 'string' ? { status: opts.health } : opts.health || { status: 'up' };
const offer = {
const offer = {
@@ -42,7 +45,7 @@ function toOffer(harness, agent, opts = {}) {
@@ -42,7 +45,7 @@ function toOffer(harness, agent, opts = {}) {
provider: agent.provider || harness.provider,
provider: agent.provider || harness.provider,
region: 'global',
region: 'global',
trust: opts.trust || 'external',
trust: opts.trust || 'external',
- capabilities: [...tasks.map((task) => `task:${task}`), ...Object.keys(HARNESS_FLAGS).filter((name) => caps[name] === true).map((name) => HARNESS_FLAGS[name]), ...(caps.runtimes || []).map((runtime) => `runtime:${runtime}`)],
+ capabilities: [...tasks.map((task) => `task:${task}`), ...Object.keys(HARNESS_FLAGS).filter((name) => caps[name] === true).map((name) => HARNESS_FLAGS[name]), ...(caps.runtimes || []).map((kind) => `runtime:${kind}`), ...fidelity],
capacity: { workers: { harness: limits.max_concurrent_runs } },
capacity: { workers: { harness: limits.max_concurrent_runs } },
health,
health,
pricing: { model: 'per-operation', unit: 'token', marginal_usd_per_unit: agent.price_per_1k_tokens.fresh_usd / 1000 },
pricing: { model: 'per-operation', unit: 'token', marginal_usd_per_unit: agent.price_per_1k_tokens.fresh_usd / 1000 },
new file mode 100644
new file mode 100644
@@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
+'use strict';
+/**
+ * The runtime capability manifest (engine/runtime.js): every catalog harness declares what it can do, every offer
+ * carries it as `harness:<field>-<value>` capabilities, and both routers (placement and the tier router) exclude a
+ * harness that does not declare what the task needs — no provider-name conditional in either.
+ */
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+const { createHarnesses } = require('../server/domain/harnesses');
+const { toOffer, route } = require('../server/domain/harness-placement');
+const runtime = require('../engine/runtime');
+const { pick } = require('../engine/router');
+const { check, done } = require('./helpers/boot');
+
+const catalogPath = path.join(__dirname, '..', 'server', 'data', 'harness-offers.json');
+const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
+
+(async () => {
+ await check('every catalog harness declares every field, with a known value', () => {
+ for (const row of rows) {
+ const manifest = runtime.forHarness(row.id);
+ assert.ok(manifest, `${row.id} has no runtime manifest`);
+ assert.deepStrictEqual(Object.keys(manifest).sort(), Object.keys(runtime.FIELDS).sort(), row.id);
+ for (const [field, values] of Object.entries(runtime.FIELDS)) {
+ assert.ok(values.includes(manifest[field]), `${row.id}.${field}: ${manifest[field]} is not one of ${values.join('|')}`);
+ }
+ }
+ assert.deepStrictEqual(Object.keys(runtime.MANIFEST).sort(), rows.map((row) => row.id).sort(), 'no manifest without a catalog row');
+ });
+
+ await check('a manifest becomes one capability per field, and fits() reads it', () => {
+ assert.deepStrictEqual(runtime.capabilities(runtime.forHarness('claude-code')), [
+ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',
+ 'harness:subagents-native', 'harness:context-handoff-session', 'harness:enforcement-native',
+ ]);
+ assert.deepStrictEqual(runtime.requirements({ steering: 'resume', subagents: 'native' }), ['harness:steering-resume', 'harness:subagents-native']);
+ assert.strictEqual(runtime.fits('claude-code', { subagents: 'native' }), true);
+ assert.strictEqual(runtime.fits('codex', { subagents: 'native' }), false);
+ assert.strictEqual(runtime.fits('cx', ['harness:steering-resume']), true, 'the portal runner id names the same harness');
+ assert.strictEqual(runtime.fits('pg', ['harness:steering-resume']), false, 'an unknown runner declares nothing');
+ assert.strictEqual(runtime.fits('pg', []), true, 'nothing required, nothing to exclude');
+ });
+
+ await check('every offer carries the manifest as capabilities the placement router can require', () => {
+ const harnesses = createHarnesses({ catalogPath });
+ for (const harness of harnesses.list()) {
+ const offer = toOffer(harness, harnesses.agents(harness.id)[0]);
+ for (const cap of runtime.capabilities(runtime.forHarness(harness.id))) {
+ assert.ok(offer.capabilities.includes(cap), `${offer.offer_id} lacks ${cap}`);
+ }
+ }
+ const r = route({ task: 'edit', extra: { capabilities: ['harness:subagents-native'] }, harnesses, now: Date.now() });
+ assert.strictEqual(r.selected.split(':')[0], 'claude-code');
+ for (const id of ['codex:codex-default', 'opencode:opencode-default', 'aider:aider-default', 'deepseek:deepseek-chat']) {
+ assert.deepStrictEqual(r.candidates.find((c) => c.id === id), { id, included: false, reason: 'lacks harness:subagents-native' });
+ }
+ const none = route({ task: 'edit', extra: { capabilities: ['harness:checkpointing-git'] }, harnesses, now: Date.now() });
+ assert.strictEqual(none.selected, null, 'no harness declares checkpointing yet');
+ assert.ok(none.candidates.every((c) => c.reason === 'lacks harness:checkpointing-git'));
+ });
+
+ await check('the tier router skips a ladder rung its manifest cannot meet', () => {
+ const ctx = {
+ cfg: { subagents: { mm: { enabled: true, max: 2 }, cm: { enabled: true, max: 2 } } },
+ health: { mm: { state: 'ok' }, cm: { state: 'ok' } },
+ counts: {}, models: {}, runs: [],
+ ladder: { basic: [['mm', null, 'high'], ['cm', null, 'high']] },
+ };
+ assert.strictEqual(pick('Fix the parser', ctx).runner, 'mm', 'no requirement: the first rung stays');
+ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: ['harness:subagents-native'] }).runner, 'cm');
+ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: { steering: 'resume' } }).runner, 'mm');
+ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: ['harness:subagents-native'], ladder: { basic: [['mm', null, 'high']] } }), null);
+ });
+
+ done();
+})().catch((e) => { console.error(e); process.exit(1); });
@@ -40,7 +40,9 @@ function priced(row, freshUsd) {
@@ -40,7 +40,9 @@ function priced(row, freshUsd) {
provider: 'example',
provider: 'example',
region: 'global',
region: 'global',
trust: 'partner',
trust: 'partner',
- capabilities: ['task:edit', 'harness:host-access', 'harness:mcp', 'harness:resume', 'harness:edit', 'harness:tools', 'runtime:code'],
+ capabilities: ['task:edit', 'harness:host-access', 'harness:mcp', 'harness:resume', 'harness:edit', 'harness:tools', 'runtime:code',
+ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',
+ 'harness:subagents-none', 'harness:context-handoff-session', 'harness:enforcement-client-boundary'],
capacity: { workers: { harness: 2 } },
capacity: { workers: { harness: 2 } },
health: { status: 'degraded' },
health: { status: 'degraded' },
pricing: { model: 'per-operation', unit: 'token', marginal_usd_per_unit: 0.003 / 1000 },
pricing: { model: 'per-operation', unit: 'token', marginal_usd_per_unit: 0.003 / 1000 },
@@ -52,7 +54,9 @@ function priced(row, freshUsd) {
@@ -52,7 +54,9 @@ function priced(row, freshUsd) {
const deepseek = rows.find((row) => row.id === 'deepseek');
const deepseek = rows.find((row) => row.id === 'deepseek');
const offer = toOffer(deepseek, deepseek.agents[0]);
const offer = toOffer(deepseek, deepseek.agents[0]);
assert.strictEqual(offer.offer_id, 'deepseek:deepseek-chat');
assert.strictEqual(offer.offer_id, 'deepseek:deepseek-chat');
- assert.deepStrictEqual(offer.capabilities, ['task:edit', 'task:review', 'harness:host-access', 'harness:resume', 'harness:edit', 'harness:review', 'harness:tools']);
+ assert.deepStrictEqual(offer.capabilities, ['task:edit', 'task:review', 'harness:host-access', 'harness:resume', 'harness:edit', 'harness:review', 'harness:tools',
+ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',
+ 'harness:subagents-none', 'harness:context-handoff-session', 'harness:enforcement-client-boundary']);
assert.strictEqual(offer.trust, 'external');
assert.strictEqual(offer.trust, 'external');
assert.deepStrictEqual(offer.health, { status: 'up' });
assert.deepStrictEqual(offer.health, { status: 'up' });
assert.strictEqual('latency_ms' in offer, false);
assert.strictEqual('latency_ms' in offer, false);
@@ -73,7 +77,9 @@ function priced(row, freshUsd) {
@@ -73,7 +77,9 @@ function priced(row, freshUsd) {
}
}
const browsing = { ...rows[0], task_capabilities: ['edit', 'browse'] };
const browsing = { ...rows[0], task_capabilities: ['edit', 'browse'] };
const offer = toOffer(browsing, browsing.agents[0], { now });
const offer = toOffer(browsing, browsing.agents[0], { now });
- assert.deepStrictEqual(offer.capabilities, ['task:edit', 'task:browse', 'harness:host-access', 'harness:mcp', 'harness:long-autonomy', 'harness:resume', 'harness:edit', 'harness:review', 'harness:tools', 'harness:vision', 'runtime:code']);
+ assert.deepStrictEqual(offer.capabilities, ['task:edit', 'task:browse', 'harness:host-access', 'harness:mcp', 'harness:long-autonomy', 'harness:resume', 'harness:edit', 'harness:review', 'harness:tools', 'harness:vision', 'runtime:code',
+ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',
+ 'harness:subagents-native', 'harness:context-handoff-session', 'harness:enforcement-native']);
assert.ok(contracts.validate('platform.resource-offer@1', JSON.parse(JSON.stringify(offer))).valid);
assert.ok(contracts.validate('platform.resource-offer@1', JSON.parse(JSON.stringify(offer))).valid);
});
});
new file mode 100644
new file mode 100644
@@ -0,0 +1,77 @@
@@ -0,0 +1,77 @@
+'use strict';
+/**
+ * The runtime capability manifest (engine/runtime.js): every catalog harness declares what it can do, every offer
+ * carries it as `harness:<field>-<value>` capabilities, and both routers (placement and the tier router) exclude a
+ * harness that does not declare what the task needs — no provider-name conditional in either.
+ */
+const assert = require('assert');
+const fs = require('fs');
+const path = require('path');
+const { createHarnesses } = require('../server/domain/harnesses');
+const { toOffer, route } = require('../server/domain/harness-placement');
+const runtime = require('../engine/runtime');
+const { pick } = require('../engine/router');
+const { check, done } = require('./helpers/boot');
+
+const catalogPath = path.join(__dirname, '..', 'server', 'data', 'harness-offers.json');
+const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
+
+(async () => {
+ await check('every catalog harness declares every field, with a known value', () => {
+ for (const row of rows) {
+ const manifest = runtime.forHarness(row.id);
+ assert.ok(manifest, `${row.id} has no runtime manifest`);
+ assert.deepStrictEqual(Object.keys(manifest).sort(), Object.keys(runtime.FIELDS).sort(), row.id);
+ for (const [field, values] of Object.entries(runtime.FIELDS)) {
+ assert.ok(values.includes(manifest[field]), `${row.id}.${field}: ${manifest[field]} is not one of ${values.join('|')}`);
+ }
+ }
+ assert.deepStrictEqual(Object.keys(runtime.MANIFEST).sort(), rows.map((row) => row.id).sort(), 'no manifest without a catalog row');
+ });
+
+ await check('a manifest becomes one capability per field, and fits() reads it', () => {
+ assert.deepStrictEqual(runtime.capabilities(runtime.forHarness('claude-code')), [
+ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',
+ 'harness:subagents-native', 'harness:context-handoff-session', 'harness:enforcement-native',
+ ]);
+ assert.deepStrictEqual(runtime.requirements({ steering: 'resume', subagents: 'native' }), ['harness:steering-resume', 'harness:subagents-native']);
+ assert.strictEqual(runtime.fits('claude-code', { subagents: 'native' }), true);
+ assert.strictEqual(runtime.fits('codex', { subagents: 'native' }), false);
+ assert.strictEqual(runtime.fits('cx', ['harness:steering-resume']), true, 'the portal runner id names the same harness');
+ assert.strictEqual(runtime.fits('pg', ['harness:steering-resume']), false, 'an unknown runner declares nothing');
+ assert.strictEqual(runtime.fits('pg', []), true, 'nothing required, nothing to exclude');
+ });
+
+ await check('every offer carries the manifest as capabilities the placement router can require', () => {
+ const harnesses = createHarnesses({ catalogPath });
+ for (const harness of harnesses.list()) {
+ const offer = toOffer(harness, harnesses.agents(harness.id)[0]);
+ for (const cap of runtime.capabilities(runtime.forHarness(harness.id))) {
+ assert.ok(offer.capabilities.includes(cap), `${offer.offer_id} lacks ${cap}`);
+ }
+ }
+ const r = route({ task: 'edit', extra: { capabilities: ['harness:subagents-native'] }, harnesses, now: Date.now() });
+ assert.strictEqual(r.selected.split(':')[0], 'claude-code');
+ for (const id of ['codex:codex-default', 'opencode:opencode-default', 'aider:aider-default', 'deepseek:deepseek-chat']) {
+ assert.deepStrictEqual(r.candidates.find((c) => c.id === id), { id, included: false, reason: 'lacks harness:subagents-native' });
+ }
+ const none = route({ task: 'edit', extra: { capabilities: ['harness:checkpointing-git'] }, harnesses, now: Date.now() });
+ assert.strictEqual(none.selected, null, 'no harness declares checkpointing yet');
+ assert.ok(none.candidates.every((c) => c.reason === 'lacks harness:checkpointing-git'));
+ });
+
+ await check('the tier router skips a ladder rung its manifest cannot meet', () => {
+ const ctx = {
+ cfg: { subagents: { mm: { enabled: true, max: 2 }, cm: { enabled: true, max: 2 } } },
+ health: { mm: { state: 'ok' }, cm: { state: 'ok' } },
+ counts: {}, models: {}, runs: [],
+ ladder: { basic: [['mm', null, 'high'], ['cm', null, 'high']] },
+ };
+ assert.strictEqual(pick('Fix the parser', ctx).runner, 'mm', 'no requirement: the first rung stays');
+ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: ['harness:subagents-native'] }).runner, 'cm');
+ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: { steering: 'resume' } }).runner, 'mm');
+ assert.strictEqual(pick('Fix the parser', { ...ctx, needs: ['harness:subagents-native'], ladder: { basic: [['mm', null, 'high']] } }), null);
+ });
+
+ done();
+})().catch((e) => { console.error(e); process.exit(1); });