Skip to content
Merged
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
25 changes: 20 additions & 5 deletions integrations/openclaw-agent-memory/CLAW_HUB_PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)" \
Expand Down Expand Up @@ -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)" \
Expand Down Expand Up @@ -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.

Expand Down
14 changes: 14 additions & 0 deletions integrations/openclaw-agent-memory/RELEASE_NOTES_0.1.5.md
Original file line number Diff line number Diff line change
@@ -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 `{}`.
14 changes: 13 additions & 1 deletion integrations/openclaw-agent-memory/plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down Expand Up @@ -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.

Expand Down
208 changes: 168 additions & 40 deletions integrations/openclaw-agent-memory/plugin/dist/index.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 : {}
}
}
});
Expand All @@ -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 || {};
Expand All @@ -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"
Expand Down Expand Up @@ -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, {
Expand All @@ -176,43 +304,43 @@ 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)
});
registerTool(api, {
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;
Expand All @@ -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)
});
}
Expand Down
Loading
Loading