Skip to content

Commit c8c5fa4

Browse files
Fix OB1 Agent Memory OpenClaw tool schemas
Fixes the OpenClaw/Claude argument-generation issue for OB1 Agent Memory recall/write-back by replacing patternProperties-only TypeBox record schemas with explicit object properties. Adds schema regression validation, updates 0.1.5 package/release docs, and rebuilds the plugin bundle. Admin-merged after local review because the PR was authored by the same GitHub user and could not receive a self-approval. Checks were green before merge.
1 parent ef64fec commit c8c5fa4

10 files changed

Lines changed: 426 additions & 57 deletions

File tree

integrations/openclaw-agent-memory/CLAW_HUB_PUBLISHING.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,21 @@ npx -y clawhub@0.12.2 package publish integrations/openclaw-agent-memory/plugin
115115
--source-path integrations/openclaw-agent-memory/plugin
116116
```
117117

118+
## Prepared 0.1.5 Schema Fix
119+
120+
Prepared on 2026-05-22:
121+
122+
- Package version bumped to `0.1.5`.
123+
- `openbrain_recall` and `openbrain_writeback` now expose explicit TypeBox
124+
object properties instead of patternProperties-only record schemas.
125+
- `npm run schema:check` passed.
126+
- `npm run build` passed from the plugin package.
127+
- `npm pack --dry-run` produced `natebjones-ob1-agent-memory-0.1.5.tgz`
128+
with built `dist/index.js`, schema check script, source files, bundled skill
129+
files, and native smoke script.
130+
- `clawhub package publish --dry-run --json` passed for
131+
`@natebjones/ob1-agent-memory` version `0.1.5`.
132+
118133
License note: the OB1 repository is `FSL-1.1-MIT`. ClawHub requires public
119134
skills to be `MIT-0`, so the standalone skill files in
120135
[../../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
147162
--family code-plugin \
148163
--name @natebjones/ob1-agent-memory \
149164
--display-name "NBJ OB1 Agent Memory for OpenClaw" \
150-
--version 0.1.1 \
151-
--changelog "Package installability fix: publish plugin package with latest tag so OpenClaw can install typed OB1 Agent Memory tools from ClawHub." \
165+
--version 0.1.5 \
166+
--changelog "OpenClaw/Claude compatibility fix: recall and write-back now expose explicit tool parameter properties instead of patternProperties-only schemas." \
152167
--tags latest,nbj,nate-jones,ob1,openbrain,agent-memory,openclaw,provenance \
153168
--source-repo NateBJones-Projects/OB1 \
154169
--source-commit "$(git rev-parse HEAD)" \
@@ -184,8 +199,8 @@ npx -y clawhub@0.12.2 package publish integrations/openclaw-agent-memory/plugin
184199
--family code-plugin \
185200
--name @natebjones/ob1-agent-memory \
186201
--display-name "NBJ OB1 Agent Memory for OpenClaw" \
187-
--version 0.1.1 \
188-
--changelog "Package installability fix: publish plugin package with latest tag so OpenClaw can install typed OB1 Agent Memory tools from ClawHub." \
202+
--version 0.1.5 \
203+
--changelog "OpenClaw/Claude compatibility fix: recall and write-back now expose explicit tool parameter properties instead of patternProperties-only schemas." \
189204
--tags latest,nbj,nate-jones,ob1,openbrain,agent-memory,openclaw,provenance \
190205
--source-repo NateBJones-Projects/OB1 \
191206
--source-commit "$(git rev-parse HEAD)" \
@@ -242,7 +257,7 @@ openclaw plugins install clawhub:@natebjones/ob1-agent-memory
242257

243258
## Release Notes
244259

245-
See [RELEASE_NOTES_0.1.0.md](./RELEASE_NOTES_0.1.0.md).
260+
See [RELEASE_NOTES_0.1.5.md](./RELEASE_NOTES_0.1.5.md).
246261

247262
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.
248263

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# NBJ OB1 Agent Memory for OpenClaw 0.1.5
2+
3+
Tool-argument compatibility fix for OpenClaw and Claude.
4+
5+
## Changed
6+
7+
- Replaces generic record schemas for `openbrain_recall` and `openbrain_writeback` with explicit TypeBox object schemas.
8+
- Keeps recall and write-back API payloads runtime-neutral while making the OpenClaw tool contract model-friendly.
9+
- Injects OpenClaw schema versions and configured workspace defaults in the plugin client.
10+
- Adds a schema regression check so recall and write-back cannot silently regress to patternProperties-only tool schemas.
11+
12+
## Verification Target
13+
14+
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 `{}`.

integrations/openclaw-agent-memory/plugin/README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ Built by Nate B. Jones / OB1. Follow Nate for practical AI systems, agent workfl
66

77
## Install
88

9+
Current OpenClaw `2026.5.2` installs the ClawHub-hosted package cleanly from the published tarball:
10+
11+
```bash
12+
curl -fsSL \
13+
https://clawhub.ai/api/npm/@natebjones/ob1-agent-memory/-/natebjones-ob1-agent-memory-0.1.5.tgz \
14+
-o natebjones-ob1-agent-memory-0.1.5.tgz
15+
16+
openclaw plugins install ./natebjones-ob1-agent-memory-0.1.5.tgz
17+
```
18+
19+
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:
20+
921
```bash
1022
openclaw plugins install clawhub:@natebjones/ob1-agent-memory
1123
```
@@ -62,7 +74,7 @@ openclaw --profile ob1-agent-memory plugins install . --link
6274
}
6375
```
6476

65-
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.
77+
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.
6678

6779
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.
6880

integrations/openclaw-agent-memory/plugin/dist/index.js

Lines changed: 168 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
// src/index.ts
2-
import { Type } from "typebox";
2+
import { Type as Type2 } from "typebox";
33
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
44
import { resolveConfiguredSecretInputString } from "openclaw/plugin-sdk/secret-input-runtime";
55

66
// src/client.ts
7+
function requestInput(input) {
8+
return input && typeof input === "object" ? input : {};
9+
}
10+
function projectIdFrom(input, configuredProjectId) {
11+
if (configuredProjectId) return configuredProjectId;
12+
if (typeof input.project_id === "string" || input.project_id === null) return input.project_id;
13+
return null;
14+
}
715
var AgentMemoryClient = class {
816
constructor(config) {
917
this.config = config;
@@ -32,31 +40,37 @@ var AgentMemoryClient = class {
3240
return data;
3341
}
3442
recall(input) {
43+
const request = requestInput(input);
44+
const projectId = projectIdFrom(request, this.config.projectId);
3545
return this.request("/recall", {
3646
method: "POST",
3747
body: {
48+
...request,
49+
schema_version: "openbrain.openclaw.recall.v1",
3850
workspace_id: this.config.workspaceId,
39-
project_id: this.config.projectId ?? null,
40-
...input,
51+
project_id: projectId,
4152
scope: {
4253
include_unconfirmed: this.config.includeUnconfirmedRecall ?? false,
43-
...typeof input.scope === "object" && input.scope ? input.scope : {}
54+
...typeof request.scope === "object" && request.scope ? request.scope : {}
4455
}
4556
}
4657
});
4758
}
4859
writeback(input) {
60+
const request = requestInput(input);
61+
const projectId = projectIdFrom(request, this.config.projectId);
4962
return this.request("/writeback", {
5063
method: "POST",
5164
body: {
65+
...request,
66+
schema_version: "openbrain.openclaw.writeback.v1",
5267
workspace_id: this.config.workspaceId,
53-
project_id: this.config.projectId ?? null,
54-
...input,
68+
project_id: projectId,
5569
provenance: {
5670
default_status: "generated",
5771
confidence: 0.5,
5872
requires_review: this.config.requireReviewByDefault ?? true,
59-
...typeof input.provenance === "object" && input.provenance ? input.provenance : {}
73+
...typeof request.provenance === "object" && request.provenance ? request.provenance : {}
6074
}
6175
}
6276
});
@@ -82,6 +96,120 @@ var AgentMemoryClient = class {
8296
}
8397
};
8498

99+
// src/tool-schemas.js
100+
import { Type } from "typebox";
101+
var schemaVersion = (value) => Type.Optional(Type.Literal(value));
102+
var nullableString = () => Type.Union([Type.String(), Type.Null()]);
103+
var optionalNullableString = () => Type.Optional(nullableString());
104+
var optionalStringArray = () => Type.Optional(Type.Array(Type.String()));
105+
var optionalNullableInteger = () => Type.Optional(Type.Union([Type.Integer({ minimum: 1 }), Type.Null()]));
106+
var channelSchema = Type.Object({
107+
kind: Type.Optional(Type.String()),
108+
id: optionalNullableString(),
109+
thread_id: optionalNullableString()
110+
});
111+
var runtimeSchema = Type.Object({
112+
name: Type.Optional(Type.String()),
113+
version: optionalNullableString()
114+
});
115+
var entitiesSchema = Type.Object({
116+
people: optionalStringArray(),
117+
orgs: optionalStringArray(),
118+
repos: optionalStringArray(),
119+
files: optionalStringArray(),
120+
customers: optionalStringArray(),
121+
topics: optionalStringArray()
122+
});
123+
var recallParameters = Type.Object({
124+
schema_version: schemaVersion("openbrain.openclaw.recall.v1"),
125+
project_id: optionalNullableString(),
126+
task_id: optionalNullableString(),
127+
flow_id: optionalNullableString(),
128+
task_type: optionalNullableString(),
129+
channel: Type.Optional(channelSchema),
130+
runtime: Type.Optional(runtimeSchema),
131+
model_intent: Type.Optional(Type.Object({
132+
provider: optionalNullableString(),
133+
model: optionalNullableString()
134+
})),
135+
query: Type.String(),
136+
entities: Type.Optional(entitiesSchema),
137+
scope: Type.Optional(Type.Object({
138+
visibility: optionalNullableString(),
139+
project_only: Type.Optional(Type.Boolean()),
140+
include_unconfirmed: Type.Optional(Type.Boolean()),
141+
include_stale: Type.Optional(Type.Boolean())
142+
})),
143+
limits: Type.Optional(Type.Object({
144+
max_items: Type.Optional(Type.Integer({ minimum: 1, maximum: 50 })),
145+
max_tokens: Type.Optional(Type.Integer({ minimum: 256, maximum: 2e4 })),
146+
recency_days: optionalNullableInteger()
147+
})),
148+
sensitivity: Type.Optional(Type.Object({
149+
contains_code: Type.Optional(Type.Boolean()),
150+
contains_customer_data: Type.Optional(Type.Boolean()),
151+
contains_private_meeting_data: Type.Optional(Type.Boolean())
152+
}))
153+
});
154+
var memoryPayloadSchema = Type.Object({
155+
decisions: optionalStringArray(),
156+
outputs: optionalStringArray(),
157+
lessons: optionalStringArray(),
158+
constraints: optionalStringArray(),
159+
unresolved_questions: optionalStringArray(),
160+
next_steps: optionalStringArray(),
161+
failures: optionalStringArray(),
162+
artifacts: Type.Optional(Type.Array(Type.Object({
163+
kind: Type.String(),
164+
uri: Type.String(),
165+
description: optionalNullableString()
166+
}))),
167+
entities: Type.Optional(entitiesSchema)
168+
});
169+
var writebackParameters = Type.Object({
170+
schema_version: schemaVersion("openbrain.openclaw.writeback.v1"),
171+
project_id: optionalNullableString(),
172+
task_id: optionalNullableString(),
173+
flow_id: optionalNullableString(),
174+
step_id: optionalNullableString(),
175+
idempotency_key: optionalNullableString(),
176+
content_hash: optionalNullableString(),
177+
channel: Type.Optional(channelSchema),
178+
runtime: Type.Optional(runtimeSchema),
179+
models_used: Type.Optional(Type.Array(Type.Object({
180+
provider: Type.String(),
181+
model: Type.String(),
182+
role: Type.String()
183+
}))),
184+
source_refs: Type.Optional(Type.Array(Type.Object({
185+
kind: Type.String(),
186+
uri: optionalNullableString(),
187+
title: optionalNullableString(),
188+
timestamp: optionalNullableString()
189+
}))),
190+
memory_payload: memoryPayloadSchema,
191+
provenance: Type.Optional(Type.Object({
192+
default_status: Type.Optional(Type.Union([
193+
Type.Literal("observed"),
194+
Type.Literal("inferred"),
195+
Type.Literal("user_confirmed"),
196+
Type.Literal("imported"),
197+
Type.Literal("generated")
198+
])),
199+
confidence: Type.Optional(Type.Number({ minimum: 0, maximum: 1 })),
200+
requires_review: Type.Optional(Type.Boolean())
201+
})),
202+
retention: Type.Optional(Type.Object({
203+
ttl_days: optionalNullableInteger(),
204+
stale_after_days: optionalNullableInteger()
205+
})),
206+
visibility: Type.Optional(Type.Object({
207+
workspace: optionalNullableString(),
208+
project: optionalNullableString(),
209+
channel: optionalNullableString()
210+
}))
211+
});
212+
85213
// src/index.ts
86214
async function clientFromApi(api) {
87215
const raw = api.pluginConfig || {};
@@ -93,7 +221,7 @@ async function clientFromApi(api) {
93221
}
94222
const accessKey = await resolveConfiguredSecretInputString({
95223
config: api.config || {},
96-
env: process.env,
224+
env: {},
97225
value: raw.accessKey,
98226
path: "plugins.entries.nbj-ob1-agent-memory.config.accessKey",
99227
unresolvedReasonStyle: "detailed"
@@ -145,26 +273,26 @@ var index_default = definePluginEntry({
145273
name: "openbrain_recall",
146274
label: "NBJ OB1 recall",
147275
description: "Recall scoped Nate Jones OB1 Agent Memory before meaningful work begins.",
148-
parameters: Type.Record(Type.String(), Type.Any()),
276+
parameters: recallParameters,
149277
run: (client, input) => client.recall(input)
150278
});
151279
registerTool(api, {
152280
name: "openbrain_writeback",
153281
label: "NBJ OB1 write-back",
154282
description: "Write compact, provenance-labeled Nate Jones OB1 Agent Memory after work finishes.",
155-
parameters: Type.Record(Type.String(), Type.Any()),
283+
parameters: writebackParameters,
156284
run: (client, input) => client.writeback(input)
157285
});
158286
registerTool(api, {
159287
name: "openbrain_report_usage",
160288
label: "NBJ OB1 report usage",
161289
description: "Report which recalled memories were used or ignored.",
162-
parameters: Type.Object({
163-
request_id: Type.String(),
164-
used_memory_ids: Type.Optional(Type.Array(Type.String())),
165-
ignored: Type.Optional(Type.Array(Type.Object({
166-
memory_id: Type.String(),
167-
reason: Type.Optional(Type.String())
290+
parameters: Type2.Object({
291+
request_id: Type2.String(),
292+
used_memory_ids: Type2.Optional(Type2.Array(Type2.String())),
293+
ignored: Type2.Optional(Type2.Array(Type2.Object({
294+
memory_id: Type2.String(),
295+
reason: Type2.Optional(Type2.String())
168296
})))
169297
}),
170298
run: (client, input) => client.reportUsage(input.request_id, {
@@ -176,43 +304,43 @@ var index_default = definePluginEntry({
176304
name: "openbrain_inspect_memory",
177305
label: "NBJ OB1 inspect memory",
178306
description: "Inspect one Nate Jones OB1 Agent Memory record, including provenance and source references.",
179-
parameters: Type.Object({ memory_id: Type.String() }),
307+
parameters: Type2.Object({ memory_id: Type2.String() }),
180308
run: (client, input) => client.inspectMemory(input.memory_id)
181309
});
182310
registerTool(api, {
183311
name: "openbrain_list_review_queue",
184312
label: "NBJ OB1 review queue",
185313
description: "List agent-written memories pending human review.",
186-
parameters: Type.Object({
187-
workspace_id: Type.Optional(Type.String()),
188-
project_id: Type.Optional(Type.String())
314+
parameters: Type2.Object({
315+
workspace_id: Type2.Optional(Type2.String()),
316+
project_id: Type2.Optional(Type2.String())
189317
}),
190318
run: (client, input) => client.listReviewQueue(input)
191319
});
192320
registerTool(api, {
193321
name: "openbrain_review_memory",
194322
label: "NBJ OB1 review memory",
195323
description: "Confirm, edit, evidence-only, restrict, stale, dispute, supersede, or reject a memory.",
196-
parameters: Type.Object({
197-
memory_id: Type.String(),
198-
action: Type.Union([
199-
Type.Literal("confirm"),
200-
Type.Literal("edit"),
201-
Type.Literal("evidence_only"),
202-
Type.Literal("restrict_scope"),
203-
Type.Literal("mark_stale"),
204-
Type.Literal("merge"),
205-
Type.Literal("reject"),
206-
Type.Literal("dispute"),
207-
Type.Literal("supersede")
324+
parameters: Type2.Object({
325+
memory_id: Type2.String(),
326+
action: Type2.Union([
327+
Type2.Literal("confirm"),
328+
Type2.Literal("edit"),
329+
Type2.Literal("evidence_only"),
330+
Type2.Literal("restrict_scope"),
331+
Type2.Literal("mark_stale"),
332+
Type2.Literal("merge"),
333+
Type2.Literal("reject"),
334+
Type2.Literal("dispute"),
335+
Type2.Literal("supersede")
208336
]),
209-
actor_id: Type.Optional(Type.String()),
210-
actor_label: Type.Optional(Type.String()),
211-
notes: Type.Optional(Type.String()),
212-
content: Type.Optional(Type.String()),
213-
summary: Type.Optional(Type.String()),
214-
visibility: Type.Optional(Type.String()),
215-
related_memory_id: Type.Optional(Type.String())
337+
actor_id: Type2.Optional(Type2.String()),
338+
actor_label: Type2.Optional(Type2.String()),
339+
notes: Type2.Optional(Type2.String()),
340+
content: Type2.Optional(Type2.String()),
341+
summary: Type2.Optional(Type2.String()),
342+
visibility: Type2.Optional(Type2.String()),
343+
related_memory_id: Type2.Optional(Type2.String())
216344
}),
217345
run: (client, input) => {
218346
const { memory_id, ...body } = input;
@@ -223,7 +351,7 @@ var index_default = definePluginEntry({
223351
name: "openbrain_get_recall_trace",
224352
label: "NBJ OB1 recall trace",
225353
description: "Fetch a recall trace to debug which memories were returned and used.",
226-
parameters: Type.Object({ request_id: Type.String() }),
354+
parameters: Type2.Object({ request_id: Type2.String() }),
227355
run: (client, input) => client.getRecallTrace(input.request_id)
228356
});
229357
}

0 commit comments

Comments
 (0)