Skip to content
Open
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ All notable changes to vouch are documented here. Format follows
from the cited claims' lifecycle status. Deterministic in v1 (no LLM in the
loop). Exposed across the CLI (`vouch synthesize`), MCP (`kb_synthesize`),
and JSONL (`kb.synthesize`) surfaces (#222).
- `vouch-context` OpenClaw context engine (#228): `src/vouch/openclaw/context_engine.py`
wraps `kb.context` retrieval, the entity-salience reflex, and session hot
memory into a cited `systemPromptAddition` on every `assemble()`. The plugin
manifest declares `contracts.contextEngines: ["vouch-context"]` and registers
`adapters/openclaw/vouch-context-engine.mjs`; engine identity is advertised
on `kb.capabilities.context_engines`. Compaction stays delegated to the
legacy OpenClaw runtime (`ownsCompaction: false`).
- Entity-salience retrieval reflex: a per-session, in-memory ring buffer of
recent caller queries drives a zero-LLM substring/FTS entity pass that
attaches top-K matched claim candidates as `_meta.vouch_salience` on
Expand Down
115 changes: 115 additions & 0 deletions adapters/openclaw/vouch-context-engine.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* OpenClaw plugin entry for the vouch-context engine (#228).
*
* The host loads this module via openclaw.plugin.json → openclaw.extensions.
* Runtime assembly delegates to the Python engine through `vouch openclaw-rpc`
* so the cited synthesis path stays identical to unit tests and kb.context.
*
* Enable in openclaw.json:
* plugins.slots.contextEngine: "vouch-context"
*/

import { spawnSync } from 'node:child_process';

export const ENGINE_ID = 'vouch-context';
export const ENGINE_NAME = 'Vouch Context Engine';

/** @typedef {import('node:child_process').SpawnSyncReturns<string>} SpawnResult */

/**
* @param {string} method
* @param {Record<string, unknown>} params
* @returns {Record<string, unknown>}
*/
function callPythonEngine(method, params) {
const envelope = JSON.stringify({
id: 'openclaw',
method,
params,
});
/** @type {SpawnResult} */
const proc = spawnSync('vouch', ['openclaw-rpc'], {
Comment thread
claytonlin1110 marked this conversation as resolved.
input: envelope,
encoding: 'utf8',
env: process.env,
maxBuffer: 16 * 1024 * 1024,
});
if (proc.error) {
throw proc.error;
}
if (proc.status !== 0) {
const detail = (proc.stderr || proc.stdout || '').trim();
throw new Error(
`vouch openclaw-rpc exited ${proc.status}${detail ? `: ${detail}` : ''}`,
);
}
let parsed;
try {
parsed = JSON.parse(String(proc.stdout || '{}'));
} catch (err) {
throw new Error(`vouch openclaw-rpc returned invalid json: ${err}`);
}
if (!parsed.ok) {
const msg = parsed.error?.message || 'engine rpc failed';
throw new Error(msg);
}
return parsed.result;
}

/** @type {{ id: string; name: string; description: string; register: (api: any) => void }} */
const entry = {
id: 'vouch-context-engine',
name: 'Vouch Context Engine',
description:
'Review-gated KB context: cited retrieval + salience reflex + hot memory on every assemble()',

register(api) {
api.registerContextEngine(ENGINE_ID, (ctx) => {
const workspaceDir = ctx.workspaceDir;
const kbPath = ctx.kbPath ?? ctx.kb_path;
const agent = ctx.agent;
Comment thread
claytonlin1110 marked this conversation as resolved.
const project = ctx.project;

const baseParams = () => ({
workspaceDir,
kbPath,
agent,
project,
});

return {
info: {
id: ENGINE_ID,
name: ENGINE_NAME,
version: '0.1.0',
ownsCompaction: false,
},

async ingest({ sessionId, message, isHeartbeat }) {
return callPythonEngine('ingest', {
...baseParams(),
sessionId,
message,
isHeartbeat,
});
},

async assemble(params) {
return callPythonEngine('assemble', {
...baseParams(),
...params,
});
},

async compact(params) {
return callPythonEngine('compact', {
...baseParams(),
...params,
});
},
};
});
},
};

export default entry;
6 changes: 5 additions & 1 deletion openclaw.plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
"compat": {
"pluginApi": ">=2026.4.0"
},
"extensions": [
"./adapters/openclaw/vouch-context-engine.mjs"
Comment thread
claytonlin1110 marked this conversation as resolved.
],
"trust_boundary": {
"remote_callers_filesystem": "confined",
"write_tools_review_gated": true,
Expand All @@ -57,6 +60,7 @@
},
"contracts": {
"reviewGatedKB": ["vouch-kb-0.1.0"],
"mcpMethods": ["kb.capabilities", "kb.status", "kb.search", "kb.context"]
"mcpMethods": ["kb.capabilities", "kb.status", "kb.search", "kb.context"],
"contextEngines": ["vouch-context"]
Comment thread
claytonlin1110 marked this conversation as resolved.
}
}
9 changes: 9 additions & 0 deletions schemas/capabilities.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Returned by the server in response to `kb.capabilities`.",
"properties": {
"context_engines": {
"description": "OpenClaw context engines exposed (see openclaw.plugin.json)",
"items": {
"additionalProperties": true,
"type": "object"
},
"title": "Context Engines",
"type": "array"
},
"knowledge_capability": {
"additionalProperties": true,
"title": "Knowledge Capability",
Expand Down
4 changes: 3 additions & 1 deletion schemas/relation.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
"similar_to",
"blocks",
"implements",
"references"
"references",
"mentions",
"relates_to"
],
"title": "RelationType",
"type": "string"
Expand Down
2 changes: 2 additions & 0 deletions src/vouch/capabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from . import __version__
from .models import Capabilities
from .openclaw.context_engine import describe_engine

# The full method surface this implementation exposes. Keep this list in
# sync with the MCP server + JSONL server registrations — `test_capabilities`
Expand Down Expand Up @@ -91,4 +92,5 @@ def capabilities() -> Capabilities:
"env_vars": ["VOUCH_PROJECT", "VOUCH_AGENT"],
"config_path": "retrieval.scope",
},
context_engines=[describe_engine()],
)
8 changes: 8 additions & 0 deletions src/vouch/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2444,5 +2444,13 @@ def review_ui(
uvicorn.run(app, host=host, port=port, log_level="info")


@cli.command("openclaw-rpc")
def openclaw_rpc() -> None:
"""OpenClaw bridge: one JSON envelope on stdin, one on stdout (context engine)."""
from .openclaw import rpc as openclaw_rpc_mod

raise SystemExit(openclaw_rpc_mod.run_stdio())


if __name__ == "__main__":
cli()
4 changes: 4 additions & 0 deletions src/vouch/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,3 +433,7 @@ class Capabilities(BaseModel):
"config_path": "retrieval.scope",
}
)
context_engines: list[dict[str, Any]] = Field(
default_factory=list,
description="OpenClaw context engines exposed (see openclaw.plugin.json)",
)
21 changes: 21 additions & 0 deletions src/vouch/openclaw/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""OpenClaw integration for vouch."""

from .context_engine import (
ENGINE_API_VERSION,
ENGINE_ID,
ENGINE_NAME,
VouchContextEngine,
create_vouch_context_engine,
describe_engine,
engine_info,
)

__all__ = [
"ENGINE_API_VERSION",
"ENGINE_ID",
"ENGINE_NAME",
"VouchContextEngine",
"create_vouch_context_engine",
"describe_engine",
"engine_info",
]
Loading
Loading