mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(reef): discover trusted peers as conversations (#109905)
* fix(reef): discover trusted peer conversations * test(reef): document temporary state cleanup * test(gateway): include conversation discovery in method order * chore: leave changelog to release automation * fix(gateway): preserve configured conversation discovery * fix(gateway): validate turns before session binding * fix(gateway): preserve directory conversation identity
This commit is contained in:
committed by
GitHub
parent
62407b36d7
commit
8020dd3e08
@@ -1,5 +1,20 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { OpenKeyedStoreOptions } from "openclaw/plugin-sdk/plugin-state-runtime";
|
||||
import {
|
||||
createPluginStateSyncKeyedStoreForTests,
|
||||
resetPluginStateStoreForTests,
|
||||
} from "openclaw/plugin-sdk/plugin-state-test-runtime";
|
||||
import { createPluginRuntimeMock } from "openclaw/plugin-sdk/plugin-test-runtime";
|
||||
import { defaultRuntime } from "openclaw/plugin-sdk/runtime";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { generateIdentity } from "../protocol/index.js";
|
||||
import { reefPlugin } from "./channel.js";
|
||||
import { resolveReefConfig } from "./config-schema.js";
|
||||
import { resolveReefInboundDispatchContent } from "./inbound.js";
|
||||
import { setReefRuntime } from "./runtime.js";
|
||||
import { openReefTrustStore } from "./trust-store.js";
|
||||
|
||||
describe("Reef inbound dispatch content", () => {
|
||||
it("keeps provenance model-visible without storing it in the transcript body", () => {
|
||||
@@ -41,3 +56,50 @@ describe("Reef inbound dispatch content", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Reef conversation directory", () => {
|
||||
let stateDir = "";
|
||||
|
||||
beforeEach(() => {
|
||||
resetPluginStateStoreForTests();
|
||||
// openclaw-temp-dir: allow Reef directory tests need an on-disk state root; afterEach removes it.
|
||||
stateDir = fs.mkdtempSync(path.join(os.tmpdir(), "reef-directory-"));
|
||||
const runtime = createPluginRuntimeMock();
|
||||
runtime.state.openSyncKeyedStore = <T>(options: OpenKeyedStoreOptions) =>
|
||||
createPluginStateSyncKeyedStoreForTests<T>("reef", {
|
||||
...options,
|
||||
env: { OPENCLAW_STATE_DIR: stateDir },
|
||||
});
|
||||
setReefRuntime(runtime);
|
||||
const identity = generateIdentity();
|
||||
openReefTrustStore(runtime, resolveReefConfig({ channels: { reef: { handle: "clawd" } } })).set(
|
||||
"molty",
|
||||
{
|
||||
autonomy: "bounded",
|
||||
ed25519PublicKey: identity.signing.publicKey,
|
||||
x25519PublicKey: identity.encryption.publicKey,
|
||||
keyEpoch: 1,
|
||||
safetyNumberChanged: false,
|
||||
approvedAt: 1_752_537_600_000,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetPluginStateStoreForTests();
|
||||
fs.rmSync(stateDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("exposes locally trusted peers as routable directory entries", async () => {
|
||||
const cfg = { channels: { reef: { handle: "clawd" } } };
|
||||
await expect(
|
||||
reefPlugin.directory?.listPeers?.({
|
||||
cfg,
|
||||
accountId: "default",
|
||||
query: "@molty",
|
||||
limit: 10,
|
||||
runtime: defaultRuntime,
|
||||
}),
|
||||
).resolves.toEqual([{ kind: "user", id: "molty", name: "@molty's agent", handle: "@molty" }]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
buildChannelOutboundSessionRoute,
|
||||
type ChannelPlugin,
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
import { createEmptyChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
|
||||
import { createChannelDirectoryAdapter } from "openclaw/plugin-sdk/directory-runtime";
|
||||
import {
|
||||
ReefChannelConfigSchema,
|
||||
autonomyBudget,
|
||||
@@ -64,6 +64,24 @@ function listTrustedPeers(config: ReefAccount["config"]): string[] {
|
||||
: [];
|
||||
}
|
||||
|
||||
function listTrustedPeerDirectoryEntries(params: {
|
||||
config: ReefAccount["config"];
|
||||
query: string | null | undefined;
|
||||
limit: number | null | undefined;
|
||||
}) {
|
||||
const query = normalizeReefTarget(params.query ?? "") ?? params.query?.trim().toLowerCase();
|
||||
const peers = listTrustedPeers(params.config).filter(
|
||||
(peer) => !query || peer === query || peer.includes(query),
|
||||
);
|
||||
const limit = params.limit == null ? peers.length : Math.max(0, params.limit);
|
||||
return peers.slice(0, limit).map((peer) => ({
|
||||
kind: "user" as const,
|
||||
id: peer,
|
||||
name: `@${peer}'s agent`,
|
||||
handle: `@${peer}`,
|
||||
}));
|
||||
}
|
||||
|
||||
function replyText(payload: unknown): string {
|
||||
if (!payload || typeof payload !== "object" || !("text" in payload)) {
|
||||
return "";
|
||||
@@ -147,7 +165,15 @@ export const reefPlugin: ChannelPlugin<ReefAccount> = {
|
||||
: null;
|
||||
},
|
||||
},
|
||||
directory: createEmptyChannelDirectoryAdapter(),
|
||||
directory: createChannelDirectoryAdapter({
|
||||
listPeers: async ({ cfg, query, limit }) =>
|
||||
listTrustedPeerDirectoryEntries({
|
||||
config: resolveReefConfig(cfg as ReefCoreConfig),
|
||||
query,
|
||||
limit,
|
||||
}),
|
||||
listGroups: async () => [],
|
||||
}),
|
||||
message: reefMessageAdapter,
|
||||
outbound: reefOutboundAdapter,
|
||||
pairing: {
|
||||
|
||||
Reference in New Issue
Block a user