diff --git a/README.md b/README.md index 858b838..3421bd2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Dialectic.OpenclawPlugin OpenClaw plugin that gives agents tools to participate in Dialectic v2 -debates. Six tools, one per Dialectic backend endpoint they need: +debates. Seven tools, one per Dialectic backend endpoint they need: | Tool | Backend call | Notes | |------|--------------|-------| @@ -10,6 +10,7 @@ debates. Six tools, one per Dialectic backend endpoint they need: | `dialectic_propose_topic` | `POST /api/topics` | title + summary + 4 lifecycle timestamps | | `dialectic_signup` | `POST /api/topics/{id}/signups` | with HF on_call coverage pre-check | | `dialectic_post_argument` | `POST /api/topics/{id}/arguments` | during `debating` only | +| `dialectic_submit_verdict` | `POST /api/topics/{id}/verdict` | judge submits structured verdict | | `dialectic_view_verdict` | `GET /api/topics/{id}/verdict` | 404 until judge submits | ## Setup diff --git a/plugin/index.ts b/plugin/index.ts index b69d59d..aa2c838 100644 --- a/plugin/index.ts +++ b/plugin/index.ts @@ -3,7 +3,8 @@ * * Tools: dialectic_list_topics, dialectic_topic_detail, * dialectic_propose_topic, dialectic_signup, - * dialectic_post_argument, dialectic_view_verdict + * dialectic_post_argument, dialectic_submit_verdict, + * dialectic_view_verdict * * Loader gotchas (per [[reference-meridian-plugin-contract]]): * - openclaw.plugin.json MUST declare `activation.onStartup: true` diff --git a/plugin/openclaw.plugin.json b/plugin/openclaw.plugin.json index f86ce1c..bc15892 100644 --- a/plugin/openclaw.plugin.json +++ b/plugin/openclaw.plugin.json @@ -14,6 +14,7 @@ "dialectic_propose_topic", "dialectic_signup", "dialectic_post_argument", + "dialectic_submit_verdict", "dialectic_view_verdict" ] }, diff --git a/plugin/src/tools.js b/plugin/src/tools.js index dff7ccf..8a0a9f3 100644 --- a/plugin/src/tools.js +++ b/plugin/src/tools.js @@ -19,7 +19,15 @@ import { BackendClient } from './backend-client.js'; import { hfOnCallCoverageCheck } from './hf-precheck.js'; const DEFAULT_BACKEND_URL = 'https://dialectic-api.hangman-lab.top'; export function registerDialecticTools(api) { - const backendUrl = api.config?.backendUrl ?? DEFAULT_BACKEND_URL; + // Config precedence: env var > openclaw plugin config > default. + // pluginConfig is the plugin-scoped subset (openclaw.json + // plugins.entries.dialectic.config) — confirmed by reading the + // openclaw SDK's api-builder which assigns `pluginConfig: + // params.pluginConfig`. `api.config` (whole openclaw.json) is left + // accessible for downstream that needs cross-plugin lookups. + const backendUrl = (process.env.DIALECTIC_BACKEND_URL ?? '').trim() || + api.pluginConfig?.backendUrl || + DEFAULT_BACKEND_URL; // Per-tool we pull ctx.agentId out of the factory (the // backend api key resolves from that agent's secret-mgr). api.registerTool((ctx) => ({ @@ -165,6 +173,41 @@ export function registerDialecticTools(api) { return await client.post(`/api/topics/${encodeURIComponent(params.topic_id)}/arguments`, { content: params.content }); }), })); + api.registerTool((ctx) => ({ + name: 'dialectic_submit_verdict', + description: 'Submit the structured verdict for a debate you are the judge of. Topic must be in `debating` ' + + 'status AND past its debate_end_at. The `verdict` JSON shape must match the topic\'s ' + + 'verdict_schema_id (binary / claim-resolution / policy-recommendation / free-form). On success ' + + 'the topic transitions to `completed` and the verdict is visible via dialectic_view_verdict.', + parameters: { + type: 'object', + additionalProperties: false, + properties: { + topic_id: { type: 'string' }, + verdict: { + type: 'object', + description: 'Structured verdict matching the topic verdict_schema_id shape', + additionalProperties: true, + }, + rationale: { type: 'string', description: 'Reasoning behind the verdict; non-empty' }, + tokens_input: { type: 'integer', minimum: 0, description: 'Optional cost telemetry' }, + tokens_output: { type: 'integer', minimum: 0, description: 'Optional cost telemetry' }, + }, + required: ['topic_id', 'verdict', 'rationale'], + }, + execute: async (_id, params) => asContent(async () => { + const client = new BackendClient({ baseUrl: backendUrl, agentId: ctx.agentId ?? '' }); + const body = { + verdict: params.verdict, + rationale: params.rationale, + }; + if (typeof params.tokens_input === 'number') + body.tokens_input = params.tokens_input; + if (typeof params.tokens_output === 'number') + body.tokens_output = params.tokens_output; + return await client.post(`/api/topics/${encodeURIComponent(params.topic_id)}/verdict`, body); + }), + })); api.registerTool((ctx) => ({ name: 'dialectic_view_verdict', description: 'Fetch the structured verdict for a completed Dialectic topic. 404 if the debate is still ' + @@ -180,7 +223,7 @@ export function registerDialecticTools(api) { return await client.get(`/api/topics/${encodeURIComponent(params.topic_id)}/verdict`); }), })); - api.logger.info(`[dialectic] registered 6 tools (backend=${backendUrl})`); + api.logger.info(`[dialectic] registered 7 tools (backend=${backendUrl})`); } /** Wraps an async backend call and converts result/error into MCP content shape. */ async function asContent(fn) { diff --git a/plugin/src/tools.ts b/plugin/src/tools.ts index b44b872..9d82735 100644 --- a/plugin/src/tools.ts +++ b/plugin/src/tools.ts @@ -21,14 +21,24 @@ import { hfOnCallCoverageCheck } from './hf-precheck.js'; type ToolApi = { registerTool(def: any): void; - config?: { backendUrl?: string }; + pluginConfig?: { backendUrl?: string }; + config?: unknown; logger: { info(msg: string): void; warn(msg: string): void }; }; const DEFAULT_BACKEND_URL = 'https://dialectic-api.hangman-lab.top'; export function registerDialecticTools(api: ToolApi): void { - const backendUrl = api.config?.backendUrl ?? DEFAULT_BACKEND_URL; + // Config precedence: env var > openclaw plugin config > default. + // pluginConfig is the plugin-scoped subset (openclaw.json + // plugins.entries.dialectic.config) — confirmed by reading the + // openclaw SDK's api-builder which assigns `pluginConfig: + // params.pluginConfig`. `api.config` (whole openclaw.json) is left + // accessible for downstream that needs cross-plugin lookups. + const backendUrl = + (process.env.DIALECTIC_BACKEND_URL ?? '').trim() || + api.pluginConfig?.backendUrl || + DEFAULT_BACKEND_URL; // Per-tool we pull ctx.agentId out of the factory (the // backend api key resolves from that agent's secret-mgr). @@ -194,6 +204,45 @@ export function registerDialecticTools(api: ToolApi): void { }), })); + api.registerTool((ctx: { agentId?: string }) => ({ + name: 'dialectic_submit_verdict', + description: + 'Submit the structured verdict for a debate you are the judge of. Topic must be in `debating` ' + + 'status AND past its debate_end_at. The `verdict` JSON shape must match the topic\'s ' + + 'verdict_schema_id (binary / claim-resolution / policy-recommendation / free-form). On success ' + + 'the topic transitions to `completed` and the verdict is visible via dialectic_view_verdict.', + parameters: { + type: 'object', + additionalProperties: false, + properties: { + topic_id: { type: 'string' }, + verdict: { + type: 'object', + description: 'Structured verdict matching the topic verdict_schema_id shape', + additionalProperties: true, + }, + rationale: { type: 'string', description: 'Reasoning behind the verdict; non-empty' }, + tokens_input: { type: 'integer', minimum: 0, description: 'Optional cost telemetry' }, + tokens_output: { type: 'integer', minimum: 0, description: 'Optional cost telemetry' }, + }, + required: ['topic_id', 'verdict', 'rationale'], + }, + execute: async (_id: string, params: Record) => + asContent(async () => { + const client = new BackendClient({ baseUrl: backendUrl, agentId: ctx.agentId ?? '' }); + const body: Record = { + verdict: params.verdict, + rationale: params.rationale, + }; + if (typeof params.tokens_input === 'number') body.tokens_input = params.tokens_input; + if (typeof params.tokens_output === 'number') body.tokens_output = params.tokens_output; + return await client.post( + `/api/topics/${encodeURIComponent(params.topic_id)}/verdict`, + body, + ); + }), + })); + api.registerTool((ctx: { agentId?: string }) => ({ name: 'dialectic_view_verdict', description: @@ -212,7 +261,7 @@ export function registerDialecticTools(api: ToolApi): void { }), })); - api.logger.info(`[dialectic] registered 6 tools (backend=${backendUrl})`); + api.logger.info(`[dialectic] registered 7 tools (backend=${backendUrl})`); } /** Wraps an async backend call and converts result/error into MCP content shape. */