diff --git a/integrations/openclaw-agent-memory/CLAW_HUB_PUBLISHING.md b/integrations/openclaw-agent-memory/CLAW_HUB_PUBLISHING.md index 487206cc..af7b8acf 100644 --- a/integrations/openclaw-agent-memory/CLAW_HUB_PUBLISHING.md +++ b/integrations/openclaw-agent-memory/CLAW_HUB_PUBLISHING.md @@ -115,6 +115,21 @@ npx -y clawhub@0.12.2 package publish integrations/openclaw-agent-memory/plugin --source-path integrations/openclaw-agent-memory/plugin ``` +## Prepared 0.1.5 Schema Fix + +Prepared on 2026-05-22: + +- Package version bumped to `0.1.5`. +- `openbrain_recall` and `openbrain_writeback` now expose explicit TypeBox + object properties instead of patternProperties-only record schemas. +- `npm run schema:check` passed. +- `npm run build` passed from the plugin package. +- `npm pack --dry-run` produced `natebjones-ob1-agent-memory-0.1.5.tgz` + with built `dist/index.js`, schema check script, source files, bundled skill + files, and native smoke script. +- `clawhub package publish --dry-run --json` passed for + `@natebjones/ob1-agent-memory` version `0.1.5`. + License note: the OB1 repository is `FSL-1.1-MIT`. ClawHub requires public skills to be `MIT-0`, so the standalone skill files in [../../skills/openclaw-agent-memory](../../skills/openclaw-agent-memory/) are @@ -147,8 +162,8 @@ npx -y clawhub@0.12.2 package publish integrations/openclaw-agent-memory/plugin --family code-plugin \ --name @natebjones/ob1-agent-memory \ --display-name "NBJ OB1 Agent Memory for OpenClaw" \ - --version 0.1.1 \ - --changelog "Package installability fix: publish plugin package with latest tag so OpenClaw can install typed OB1 Agent Memory tools from ClawHub." \ + --version 0.1.5 \ + --changelog "OpenClaw/Claude compatibility fix: recall and write-back now expose explicit tool parameter properties instead of patternProperties-only schemas." \ --tags latest,nbj,nate-jones,ob1,openbrain,agent-memory,openclaw,provenance \ --source-repo NateBJones-Projects/OB1 \ --source-commit "$(git rev-parse HEAD)" \ @@ -184,8 +199,8 @@ npx -y clawhub@0.12.2 package publish integrations/openclaw-agent-memory/plugin --family code-plugin \ --name @natebjones/ob1-agent-memory \ --display-name "NBJ OB1 Agent Memory for OpenClaw" \ - --version 0.1.1 \ - --changelog "Package installability fix: publish plugin package with latest tag so OpenClaw can install typed OB1 Agent Memory tools from ClawHub." \ + --version 0.1.5 \ + --changelog "OpenClaw/Claude compatibility fix: recall and write-back now expose explicit tool parameter properties instead of patternProperties-only schemas." \ --tags latest,nbj,nate-jones,ob1,openbrain,agent-memory,openclaw,provenance \ --source-repo NateBJones-Projects/OB1 \ --source-commit "$(git rev-parse HEAD)" \ @@ -242,7 +257,7 @@ openclaw plugins install clawhub:@natebjones/ob1-agent-memory ## Release Notes -See [RELEASE_NOTES_0.1.0.md](./RELEASE_NOTES_0.1.0.md). +See [RELEASE_NOTES_0.1.5.md](./RELEASE_NOTES_0.1.5.md). Public release copy should always include a short Nate Jones CTA. Keep it useful-first, not hype-first: Nate gives away practical AI systems like this, and the next step is following or subscribing for more. diff --git a/integrations/openclaw-agent-memory/RELEASE_NOTES_0.1.5.md b/integrations/openclaw-agent-memory/RELEASE_NOTES_0.1.5.md new file mode 100644 index 00000000..e3656f65 --- /dev/null +++ b/integrations/openclaw-agent-memory/RELEASE_NOTES_0.1.5.md @@ -0,0 +1,14 @@ +# NBJ OB1 Agent Memory for OpenClaw 0.1.5 + +Tool-argument compatibility fix for OpenClaw and Claude. + +## Changed + +- Replaces generic record schemas for `openbrain_recall` and `openbrain_writeback` with explicit TypeBox object schemas. +- Keeps recall and write-back API payloads runtime-neutral while making the OpenClaw tool contract model-friendly. +- Injects OpenClaw schema versions and configured workspace defaults in the plugin client. +- Adds a schema regression check so recall and write-back cannot silently regress to patternProperties-only tool schemas. + +## Verification Target + +After publish, a clean OpenClaw profile should install the plugin and call all seven OB1 Agent Memory tools. The recall and write-back trajectory entries should include populated payloads instead of `{}`. diff --git a/integrations/openclaw-agent-memory/plugin/README.md b/integrations/openclaw-agent-memory/plugin/README.md index b5641acf..a592775b 100644 --- a/integrations/openclaw-agent-memory/plugin/README.md +++ b/integrations/openclaw-agent-memory/plugin/README.md @@ -6,6 +6,18 @@ Built by Nate B. Jones / OB1. Follow Nate for practical AI systems, agent workfl ## Install +Current OpenClaw `2026.5.2` installs the ClawHub-hosted package cleanly from the published tarball: + +```bash +curl -fsSL \ + https://clawhub.ai/api/npm/@natebjones/ob1-agent-memory/-/natebjones-ob1-agent-memory-0.1.5.tgz \ + -o natebjones-ob1-agent-memory-0.1.5.tgz + +openclaw plugins install ./natebjones-ob1-agent-memory-0.1.5.tgz +``` + +The package is also published on ClawHub as `@natebjones/ob1-agent-memory`. When OpenClaw's `clawhub:` resolver accepts npm-pack artifact metadata directly, this should become the normal one-line install: + ```bash openclaw plugins install clawhub:@natebjones/ob1-agent-memory ``` @@ -62,7 +74,7 @@ openclaw --profile ob1-agent-memory plugins install . --link } ``` -Configure `secrets.providers.ob1_agent_memory` with a file, env, or exec provider before enabling the plugin. The plugin resolves OpenClaw SecretRefs at tool execution time so the access key does not need to live in plaintext config. +Configure `secrets.providers.ob1_agent_memory` with a file or exec provider before enabling the plugin. The plugin resolves OpenClaw SecretRefs at tool execution time so the access key does not need to live in plaintext config. Env SecretRefs are intentionally not used in the launch package so OpenClaw's install-time safety scan does not classify the memory client as env-to-network credential forwarding. Current OpenClaw builds also require explicit `tools.allow` entries before plugin tools are exposed to the model. `plugins inspect --runtime` can show the plugin is registered even when the agent cannot see the tools, so run a native tool smoke test after enabling the plugin. diff --git a/integrations/openclaw-agent-memory/plugin/dist/index.js b/integrations/openclaw-agent-memory/plugin/dist/index.js index 8d797755..c5ed6a63 100644 --- a/integrations/openclaw-agent-memory/plugin/dist/index.js +++ b/integrations/openclaw-agent-memory/plugin/dist/index.js @@ -1,9 +1,17 @@ // src/index.ts -import { Type } from "typebox"; +import { Type as Type2 } from "typebox"; import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; import { resolveConfiguredSecretInputString } from "openclaw/plugin-sdk/secret-input-runtime"; // src/client.ts +function requestInput(input) { + return input && typeof input === "object" ? input : {}; +} +function projectIdFrom(input, configuredProjectId) { + if (configuredProjectId) return configuredProjectId; + if (typeof input.project_id === "string" || input.project_id === null) return input.project_id; + return null; +} var AgentMemoryClient = class { constructor(config) { this.config = config; @@ -32,31 +40,37 @@ var AgentMemoryClient = class { return data; } recall(input) { + const request = requestInput(input); + const projectId = projectIdFrom(request, this.config.projectId); return this.request("/recall", { method: "POST", body: { + ...request, + schema_version: "openbrain.openclaw.recall.v1", workspace_id: this.config.workspaceId, - project_id: this.config.projectId ?? null, - ...input, + project_id: projectId, scope: { include_unconfirmed: this.config.includeUnconfirmedRecall ?? false, - ...typeof input.scope === "object" && input.scope ? input.scope : {} + ...typeof request.scope === "object" && request.scope ? request.scope : {} } } }); } writeback(input) { + const request = requestInput(input); + const projectId = projectIdFrom(request, this.config.projectId); return this.request("/writeback", { method: "POST", body: { + ...request, + schema_version: "openbrain.openclaw.writeback.v1", workspace_id: this.config.workspaceId, - project_id: this.config.projectId ?? null, - ...input, + project_id: projectId, provenance: { default_status: "generated", confidence: 0.5, requires_review: this.config.requireReviewByDefault ?? true, - ...typeof input.provenance === "object" && input.provenance ? input.provenance : {} + ...typeof request.provenance === "object" && request.provenance ? request.provenance : {} } } }); @@ -82,6 +96,120 @@ var AgentMemoryClient = class { } }; +// src/tool-schemas.js +import { Type } from "typebox"; +var schemaVersion = (value) => Type.Optional(Type.Literal(value)); +var nullableString = () => Type.Union([Type.String(), Type.Null()]); +var optionalNullableString = () => Type.Optional(nullableString()); +var optionalStringArray = () => Type.Optional(Type.Array(Type.String())); +var optionalNullableInteger = () => Type.Optional(Type.Union([Type.Integer({ minimum: 1 }), Type.Null()])); +var channelSchema = Type.Object({ + kind: Type.Optional(Type.String()), + id: optionalNullableString(), + thread_id: optionalNullableString() +}); +var runtimeSchema = Type.Object({ + name: Type.Optional(Type.String()), + version: optionalNullableString() +}); +var entitiesSchema = Type.Object({ + people: optionalStringArray(), + orgs: optionalStringArray(), + repos: optionalStringArray(), + files: optionalStringArray(), + customers: optionalStringArray(), + topics: optionalStringArray() +}); +var recallParameters = Type.Object({ + schema_version: schemaVersion("openbrain.openclaw.recall.v1"), + project_id: optionalNullableString(), + task_id: optionalNullableString(), + flow_id: optionalNullableString(), + task_type: optionalNullableString(), + channel: Type.Optional(channelSchema), + runtime: Type.Optional(runtimeSchema), + model_intent: Type.Optional(Type.Object({ + provider: optionalNullableString(), + model: optionalNullableString() + })), + query: Type.String(), + entities: Type.Optional(entitiesSchema), + scope: Type.Optional(Type.Object({ + visibility: optionalNullableString(), + project_only: Type.Optional(Type.Boolean()), + include_unconfirmed: Type.Optional(Type.Boolean()), + include_stale: Type.Optional(Type.Boolean()) + })), + limits: Type.Optional(Type.Object({ + max_items: Type.Optional(Type.Integer({ minimum: 1, maximum: 50 })), + max_tokens: Type.Optional(Type.Integer({ minimum: 256, maximum: 2e4 })), + recency_days: optionalNullableInteger() + })), + sensitivity: Type.Optional(Type.Object({ + contains_code: Type.Optional(Type.Boolean()), + contains_customer_data: Type.Optional(Type.Boolean()), + contains_private_meeting_data: Type.Optional(Type.Boolean()) + })) +}); +var memoryPayloadSchema = Type.Object({ + decisions: optionalStringArray(), + outputs: optionalStringArray(), + lessons: optionalStringArray(), + constraints: optionalStringArray(), + unresolved_questions: optionalStringArray(), + next_steps: optionalStringArray(), + failures: optionalStringArray(), + artifacts: Type.Optional(Type.Array(Type.Object({ + kind: Type.String(), + uri: Type.String(), + description: optionalNullableString() + }))), + entities: Type.Optional(entitiesSchema) +}); +var writebackParameters = Type.Object({ + schema_version: schemaVersion("openbrain.openclaw.writeback.v1"), + project_id: optionalNullableString(), + task_id: optionalNullableString(), + flow_id: optionalNullableString(), + step_id: optionalNullableString(), + idempotency_key: optionalNullableString(), + content_hash: optionalNullableString(), + channel: Type.Optional(channelSchema), + runtime: Type.Optional(runtimeSchema), + models_used: Type.Optional(Type.Array(Type.Object({ + provider: Type.String(), + model: Type.String(), + role: Type.String() + }))), + source_refs: Type.Optional(Type.Array(Type.Object({ + kind: Type.String(), + uri: optionalNullableString(), + title: optionalNullableString(), + timestamp: optionalNullableString() + }))), + memory_payload: memoryPayloadSchema, + provenance: Type.Optional(Type.Object({ + default_status: Type.Optional(Type.Union([ + Type.Literal("observed"), + Type.Literal("inferred"), + Type.Literal("user_confirmed"), + Type.Literal("imported"), + Type.Literal("generated") + ])), + confidence: Type.Optional(Type.Number({ minimum: 0, maximum: 1 })), + requires_review: Type.Optional(Type.Boolean()) + })), + retention: Type.Optional(Type.Object({ + ttl_days: optionalNullableInteger(), + stale_after_days: optionalNullableInteger() + })), + visibility: Type.Optional(Type.Object({ + workspace: optionalNullableString(), + project: optionalNullableString(), + channel: optionalNullableString() + })) +}); + // src/index.ts async function clientFromApi(api) { const raw = api.pluginConfig || {}; @@ -93,7 +221,7 @@ async function clientFromApi(api) { } const accessKey = await resolveConfiguredSecretInputString({ config: api.config || {}, - env: process.env, + env: {}, value: raw.accessKey, path: "plugins.entries.nbj-ob1-agent-memory.config.accessKey", unresolvedReasonStyle: "detailed" @@ -145,26 +273,26 @@ var index_default = definePluginEntry({ name: "openbrain_recall", label: "NBJ OB1 recall", description: "Recall scoped Nate Jones OB1 Agent Memory before meaningful work begins.", - parameters: Type.Record(Type.String(), Type.Any()), + parameters: recallParameters, run: (client, input) => client.recall(input) }); registerTool(api, { name: "openbrain_writeback", label: "NBJ OB1 write-back", description: "Write compact, provenance-labeled Nate Jones OB1 Agent Memory after work finishes.", - parameters: Type.Record(Type.String(), Type.Any()), + parameters: writebackParameters, run: (client, input) => client.writeback(input) }); registerTool(api, { name: "openbrain_report_usage", label: "NBJ OB1 report usage", description: "Report which recalled memories were used or ignored.", - parameters: Type.Object({ - request_id: Type.String(), - used_memory_ids: Type.Optional(Type.Array(Type.String())), - ignored: Type.Optional(Type.Array(Type.Object({ - memory_id: Type.String(), - reason: Type.Optional(Type.String()) + parameters: Type2.Object({ + request_id: Type2.String(), + used_memory_ids: Type2.Optional(Type2.Array(Type2.String())), + ignored: Type2.Optional(Type2.Array(Type2.Object({ + memory_id: Type2.String(), + reason: Type2.Optional(Type2.String()) }))) }), run: (client, input) => client.reportUsage(input.request_id, { @@ -176,16 +304,16 @@ var index_default = definePluginEntry({ name: "openbrain_inspect_memory", label: "NBJ OB1 inspect memory", description: "Inspect one Nate Jones OB1 Agent Memory record, including provenance and source references.", - parameters: Type.Object({ memory_id: Type.String() }), + parameters: Type2.Object({ memory_id: Type2.String() }), run: (client, input) => client.inspectMemory(input.memory_id) }); registerTool(api, { name: "openbrain_list_review_queue", label: "NBJ OB1 review queue", description: "List agent-written memories pending human review.", - parameters: Type.Object({ - workspace_id: Type.Optional(Type.String()), - project_id: Type.Optional(Type.String()) + parameters: Type2.Object({ + workspace_id: Type2.Optional(Type2.String()), + project_id: Type2.Optional(Type2.String()) }), run: (client, input) => client.listReviewQueue(input) }); @@ -193,26 +321,26 @@ var index_default = definePluginEntry({ name: "openbrain_review_memory", label: "NBJ OB1 review memory", description: "Confirm, edit, evidence-only, restrict, stale, dispute, supersede, or reject a memory.", - parameters: Type.Object({ - memory_id: Type.String(), - action: Type.Union([ - Type.Literal("confirm"), - Type.Literal("edit"), - Type.Literal("evidence_only"), - Type.Literal("restrict_scope"), - Type.Literal("mark_stale"), - Type.Literal("merge"), - Type.Literal("reject"), - Type.Literal("dispute"), - Type.Literal("supersede") + parameters: Type2.Object({ + memory_id: Type2.String(), + action: Type2.Union([ + Type2.Literal("confirm"), + Type2.Literal("edit"), + Type2.Literal("evidence_only"), + Type2.Literal("restrict_scope"), + Type2.Literal("mark_stale"), + Type2.Literal("merge"), + Type2.Literal("reject"), + Type2.Literal("dispute"), + Type2.Literal("supersede") ]), - actor_id: Type.Optional(Type.String()), - actor_label: Type.Optional(Type.String()), - notes: Type.Optional(Type.String()), - content: Type.Optional(Type.String()), - summary: Type.Optional(Type.String()), - visibility: Type.Optional(Type.String()), - related_memory_id: Type.Optional(Type.String()) + actor_id: Type2.Optional(Type2.String()), + actor_label: Type2.Optional(Type2.String()), + notes: Type2.Optional(Type2.String()), + content: Type2.Optional(Type2.String()), + summary: Type2.Optional(Type2.String()), + visibility: Type2.Optional(Type2.String()), + related_memory_id: Type2.Optional(Type2.String()) }), run: (client, input) => { const { memory_id, ...body } = input; @@ -223,7 +351,7 @@ var index_default = definePluginEntry({ name: "openbrain_get_recall_trace", label: "NBJ OB1 recall trace", description: "Fetch a recall trace to debug which memories were returned and used.", - parameters: Type.Object({ request_id: Type.String() }), + parameters: Type2.Object({ request_id: Type2.String() }), run: (client, input) => client.getRecallTrace(input.request_id) }); } diff --git a/integrations/openclaw-agent-memory/plugin/openclaw.plugin.json b/integrations/openclaw-agent-memory/plugin/openclaw.plugin.json index 2c3b5c20..386f3f19 100644 --- a/integrations/openclaw-agent-memory/plugin/openclaw.plugin.json +++ b/integrations/openclaw-agent-memory/plugin/openclaw.plugin.json @@ -37,7 +37,7 @@ "properties": { "source": { "type": "string", - "enum": ["env", "file", "exec"] + "enum": ["file", "exec"] }, "provider": { "type": "string" diff --git a/integrations/openclaw-agent-memory/plugin/package.json b/integrations/openclaw-agent-memory/plugin/package.json index aefc51a3..9ccff6a7 100644 --- a/integrations/openclaw-agent-memory/plugin/package.json +++ b/integrations/openclaw-agent-memory/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@natebjones/ob1-agent-memory", - "version": "0.1.1", + "version": "0.1.5", "description": "Governed NBJ OB1 Agent Memory tools for OpenClaw: recall before work, write back after, inspect everything.", "type": "module", "license": "MIT-0", @@ -25,6 +25,7 @@ "README.md", "openclaw.plugin.json", "dist", + "scripts", "src", "skills", "smoke" @@ -34,6 +35,7 @@ }, "scripts": { "build": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node20 --external:openclaw/* --external:typebox --outfile=dist/index.js", + "schema:check": "node scripts/check-tool-schemas.mjs", "smoke:native": "bash smoke/native-openclaw-smoke.sh" }, "dependencies": { diff --git a/integrations/openclaw-agent-memory/plugin/scripts/check-tool-schemas.mjs b/integrations/openclaw-agent-memory/plugin/scripts/check-tool-schemas.mjs new file mode 100644 index 00000000..d11f2897 --- /dev/null +++ b/integrations/openclaw-agent-memory/plugin/scripts/check-tool-schemas.mjs @@ -0,0 +1,62 @@ +import { recallParameters, writebackParameters } from "../src/tool-schemas.js"; + +const checks = [ + { + name: "openbrain_recall", + schema: recallParameters, + expectedProperties: ["schema_version", "query", "entities", "scope", "limits", "sensitivity"], + }, + { + name: "openbrain_writeback", + schema: writebackParameters, + expectedProperties: ["schema_version", "memory_payload", "provenance", "retention", "visibility"], + }, +]; + +function fail(message) { + console.error(JSON.stringify({ ok: false, error: message }, null, 2)); + process.exit(1); +} + +function assertObjectProperties(name, schema, expectedProperties) { + const properties = schema?.properties || {}; + const propertyNames = Object.keys(properties); + if (schema?.type !== "object") fail(`${name} must be an object schema`); + if (propertyNames.length === 0) fail(`${name} must expose named top-level properties`); + if (schema.patternProperties && propertyNames.length === 0) { + fail(`${name} relies only on patternProperties`); + } + + const missing = expectedProperties.filter((property) => !propertyNames.includes(property)); + if (missing.length) fail(`${name} is missing expected properties: ${missing.join(", ")}`); +} + +function assertNoPatternOnlyObjects(name, schema, path = name) { + if (!schema || typeof schema !== "object") return; + + const properties = schema.properties || {}; + if (schema.type === "object" && schema.patternProperties && Object.keys(properties).length === 0) { + fail(`${path} is a patternProperties-only object schema`); + } + + for (const [propertyName, propertySchema] of Object.entries(properties)) { + assertNoPatternOnlyObjects(name, propertySchema, `${path}.${propertyName}`); + } + + if (schema.items) assertNoPatternOnlyObjects(name, schema.items, `${path}[]`); + for (const branchKey of ["anyOf", "allOf", "oneOf"]) { + for (const [index, branch] of (schema[branchKey] || []).entries()) { + assertNoPatternOnlyObjects(name, branch, `${path}.${branchKey}[${index}]`); + } + } +} + +for (const check of checks) { + assertObjectProperties(check.name, check.schema, check.expectedProperties); + assertNoPatternOnlyObjects(check.name, check.schema); +} + +console.log(JSON.stringify({ + ok: true, + checked_tools: checks.map((check) => check.name), +}, null, 2)); diff --git a/integrations/openclaw-agent-memory/plugin/src/client.ts b/integrations/openclaw-agent-memory/plugin/src/client.ts index 66211eb3..4c07b569 100644 --- a/integrations/openclaw-agent-memory/plugin/src/client.ts +++ b/integrations/openclaw-agent-memory/plugin/src/client.ts @@ -12,6 +12,16 @@ type RequestOptions = { body?: unknown; }; +function requestInput(input: Record) { + return input && typeof input === "object" ? input : {}; +} + +function projectIdFrom(input: Record, configuredProjectId?: string) { + if (configuredProjectId) return configuredProjectId; + if (typeof input.project_id === "string" || input.project_id === null) return input.project_id; + return null; +} + export class AgentMemoryClient { private endpoint: string; private accessKey: string; @@ -42,32 +52,38 @@ export class AgentMemoryClient { } recall(input: Record) { + const request = requestInput(input); + const projectId = projectIdFrom(request, this.config.projectId); return this.request("/recall", { method: "POST", body: { + ...request, + schema_version: "openbrain.openclaw.recall.v1", workspace_id: this.config.workspaceId, - project_id: this.config.projectId ?? null, - ...input, + project_id: projectId, scope: { include_unconfirmed: this.config.includeUnconfirmedRecall ?? false, - ...(typeof input.scope === "object" && input.scope ? input.scope : {}), + ...(typeof request.scope === "object" && request.scope ? request.scope : {}), }, }, }); } writeback(input: Record) { + const request = requestInput(input); + const projectId = projectIdFrom(request, this.config.projectId); return this.request("/writeback", { method: "POST", body: { + ...request, + schema_version: "openbrain.openclaw.writeback.v1", workspace_id: this.config.workspaceId, - project_id: this.config.projectId ?? null, - ...input, + project_id: projectId, provenance: { default_status: "generated", confidence: 0.5, requires_review: this.config.requireReviewByDefault ?? true, - ...(typeof input.provenance === "object" && input.provenance ? input.provenance : {}), + ...(typeof request.provenance === "object" && request.provenance ? request.provenance : {}), }, }, }); diff --git a/integrations/openclaw-agent-memory/plugin/src/index.ts b/integrations/openclaw-agent-memory/plugin/src/index.ts index 1c482f73..9f184add 100644 --- a/integrations/openclaw-agent-memory/plugin/src/index.ts +++ b/integrations/openclaw-agent-memory/plugin/src/index.ts @@ -2,6 +2,7 @@ import { Type } from "typebox"; import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; import { resolveConfiguredSecretInputString } from "openclaw/plugin-sdk/secret-input-runtime"; import { AgentMemoryClient, type AgentMemoryConfig } from "./client.js"; +import { recallParameters, writebackParameters } from "./tool-schemas.js"; async function clientFromApi(api: { pluginConfig?: unknown; config?: unknown }) { const raw = (api.pluginConfig || {}) as Record; @@ -14,7 +15,7 @@ async function clientFromApi(api: { pluginConfig?: unknown; config?: unknown }) const accessKey = await resolveConfiguredSecretInputString({ config: (api.config || {}) as any, - env: process.env, + env: {}, value: raw.accessKey, path: "plugins.entries.nbj-ob1-agent-memory.config.accessKey", unresolvedReasonStyle: "detailed", @@ -71,7 +72,7 @@ export default definePluginEntry({ name: "openbrain_recall", label: "NBJ OB1 recall", description: "Recall scoped Nate Jones OB1 Agent Memory before meaningful work begins.", - parameters: Type.Record(Type.String(), Type.Any()), + parameters: recallParameters, run: (client, input) => client.recall(input), }); @@ -79,7 +80,7 @@ export default definePluginEntry({ name: "openbrain_writeback", label: "NBJ OB1 write-back", description: "Write compact, provenance-labeled Nate Jones OB1 Agent Memory after work finishes.", - parameters: Type.Record(Type.String(), Type.Any()), + parameters: writebackParameters, run: (client, input) => client.writeback(input), }); diff --git a/integrations/openclaw-agent-memory/plugin/src/tool-schemas.js b/integrations/openclaw-agent-memory/plugin/src/tool-schemas.js new file mode 100644 index 00000000..a72201ce --- /dev/null +++ b/integrations/openclaw-agent-memory/plugin/src/tool-schemas.js @@ -0,0 +1,119 @@ +import { Type } from "typebox"; + +const schemaVersion = (value) => Type.Optional(Type.Literal(value)); +const nullableString = () => Type.Union([Type.String(), Type.Null()]); +const optionalNullableString = () => Type.Optional(nullableString()); +const optionalStringArray = () => Type.Optional(Type.Array(Type.String())); +const optionalNullableInteger = () => Type.Optional(Type.Union([Type.Integer({ minimum: 1 }), Type.Null()])); + +const channelSchema = Type.Object({ + kind: Type.Optional(Type.String()), + id: optionalNullableString(), + thread_id: optionalNullableString(), +}); + +const runtimeSchema = Type.Object({ + name: Type.Optional(Type.String()), + version: optionalNullableString(), +}); + +const entitiesSchema = Type.Object({ + people: optionalStringArray(), + orgs: optionalStringArray(), + repos: optionalStringArray(), + files: optionalStringArray(), + customers: optionalStringArray(), + topics: optionalStringArray(), +}); + +export const recallParameters = Type.Object({ + schema_version: schemaVersion("openbrain.openclaw.recall.v1"), + project_id: optionalNullableString(), + task_id: optionalNullableString(), + flow_id: optionalNullableString(), + task_type: optionalNullableString(), + channel: Type.Optional(channelSchema), + runtime: Type.Optional(runtimeSchema), + model_intent: Type.Optional(Type.Object({ + provider: optionalNullableString(), + model: optionalNullableString(), + })), + query: Type.String(), + entities: Type.Optional(entitiesSchema), + scope: Type.Optional(Type.Object({ + visibility: optionalNullableString(), + project_only: Type.Optional(Type.Boolean()), + include_unconfirmed: Type.Optional(Type.Boolean()), + include_stale: Type.Optional(Type.Boolean()), + })), + limits: Type.Optional(Type.Object({ + max_items: Type.Optional(Type.Integer({ minimum: 1, maximum: 50 })), + max_tokens: Type.Optional(Type.Integer({ minimum: 256, maximum: 20000 })), + recency_days: optionalNullableInteger(), + })), + sensitivity: Type.Optional(Type.Object({ + contains_code: Type.Optional(Type.Boolean()), + contains_customer_data: Type.Optional(Type.Boolean()), + contains_private_meeting_data: Type.Optional(Type.Boolean()), + })), +}); + +const memoryPayloadSchema = Type.Object({ + decisions: optionalStringArray(), + outputs: optionalStringArray(), + lessons: optionalStringArray(), + constraints: optionalStringArray(), + unresolved_questions: optionalStringArray(), + next_steps: optionalStringArray(), + failures: optionalStringArray(), + artifacts: Type.Optional(Type.Array(Type.Object({ + kind: Type.String(), + uri: Type.String(), + description: optionalNullableString(), + }))), + entities: Type.Optional(entitiesSchema), +}); + +export const writebackParameters = Type.Object({ + schema_version: schemaVersion("openbrain.openclaw.writeback.v1"), + project_id: optionalNullableString(), + task_id: optionalNullableString(), + flow_id: optionalNullableString(), + step_id: optionalNullableString(), + idempotency_key: optionalNullableString(), + content_hash: optionalNullableString(), + channel: Type.Optional(channelSchema), + runtime: Type.Optional(runtimeSchema), + models_used: Type.Optional(Type.Array(Type.Object({ + provider: Type.String(), + model: Type.String(), + role: Type.String(), + }))), + source_refs: Type.Optional(Type.Array(Type.Object({ + kind: Type.String(), + uri: optionalNullableString(), + title: optionalNullableString(), + timestamp: optionalNullableString(), + }))), + memory_payload: memoryPayloadSchema, + provenance: Type.Optional(Type.Object({ + default_status: Type.Optional(Type.Union([ + Type.Literal("observed"), + Type.Literal("inferred"), + Type.Literal("user_confirmed"), + Type.Literal("imported"), + Type.Literal("generated"), + ])), + confidence: Type.Optional(Type.Number({ minimum: 0, maximum: 1 })), + requires_review: Type.Optional(Type.Boolean()), + })), + retention: Type.Optional(Type.Object({ + ttl_days: optionalNullableInteger(), + stale_after_days: optionalNullableInteger(), + })), + visibility: Type.Optional(Type.Object({ + workspace: optionalNullableString(), + project: optionalNullableString(), + channel: optionalNullableString(), + })), +});