mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(agents): reduce malformed guild reply tool calls (#107474)
Co-authored-by: lonexreb <reach2shubhankar@gmail.com>
This commit is contained in:
committed by
GitHub
parent
a34bed409d
commit
df11cb761a
@@ -0,0 +1,196 @@
|
||||
import { Type, type TSchema } from "typebox";
|
||||
import type { ChannelMessageActionName } from "../../channels/plugins/types.public.js";
|
||||
import { stringEnum } from "../schema/typebox.js";
|
||||
|
||||
type SchemaProperties = Record<string, TSchema>;
|
||||
type SchemaPropertiesBuilder = () => SchemaProperties;
|
||||
|
||||
export const MESSAGE_TOOL_SEND_TEXT_DESCRIPTION =
|
||||
'Text for action="send". A send needs message or another send payload such as media, attachments, or presentation.';
|
||||
|
||||
export function buildMessageToolQuerySchemaProperties(): SchemaProperties {
|
||||
return { query: Type.Optional(Type.String()) };
|
||||
}
|
||||
|
||||
type SchemaGroup =
|
||||
| "reaction"
|
||||
| "fetch"
|
||||
| "query"
|
||||
| "poll"
|
||||
| "channelTarget"
|
||||
| "sticker"
|
||||
| "thread"
|
||||
| "event"
|
||||
| "moderation"
|
||||
| "channelManagement"
|
||||
| "presence";
|
||||
|
||||
type MessageToolSchemaBuilderOptions = {
|
||||
includePresentation: boolean;
|
||||
includeDeliveryPin: boolean;
|
||||
includeBestEffort: boolean;
|
||||
scopeToActions?: boolean;
|
||||
extraProperties?: SchemaProperties;
|
||||
};
|
||||
|
||||
export type MessageToolSchemaBuilders = {
|
||||
full: (options: MessageToolSchemaBuilderOptions) => SchemaProperties;
|
||||
base: (options: MessageToolSchemaBuilderOptions) => SchemaProperties;
|
||||
groups: Record<SchemaGroup, SchemaPropertiesBuilder>;
|
||||
};
|
||||
|
||||
const SCOPED_ACTION_GROUPS: ReadonlyArray<{
|
||||
group: SchemaGroup;
|
||||
actions: readonly ChannelMessageActionName[];
|
||||
}> = [
|
||||
{
|
||||
group: "reaction",
|
||||
actions: [
|
||||
"react",
|
||||
"reactions",
|
||||
"read",
|
||||
"edit",
|
||||
"delete",
|
||||
"unsend",
|
||||
"pin",
|
||||
"unpin",
|
||||
"reply",
|
||||
"thread-create",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "fetch",
|
||||
actions: [
|
||||
"read",
|
||||
"reactions",
|
||||
"search",
|
||||
"thread-list",
|
||||
"channel-list",
|
||||
"channel-info",
|
||||
"list-pins",
|
||||
"event-list",
|
||||
"sticker-search",
|
||||
"emoji-list",
|
||||
],
|
||||
},
|
||||
{
|
||||
// Include only actions whose handlers read query. Discord event-list historically
|
||||
// advertised query through the event schema but ignores it at dispatch.
|
||||
group: "query",
|
||||
actions: ["search", "sticker-search", "channel-list"],
|
||||
},
|
||||
{ group: "poll", actions: ["poll", "poll-vote"] },
|
||||
{
|
||||
group: "channelTarget",
|
||||
actions: [
|
||||
"search",
|
||||
"thread-list",
|
||||
"thread-create",
|
||||
"thread-reply",
|
||||
"channel-info",
|
||||
"channel-list",
|
||||
"channel-create",
|
||||
"channel-edit",
|
||||
"channel-delete",
|
||||
"channel-move",
|
||||
"category-create",
|
||||
"category-edit",
|
||||
"category-delete",
|
||||
"topic-create",
|
||||
"topic-edit",
|
||||
"permissions",
|
||||
"member-info",
|
||||
"role-info",
|
||||
"role-add",
|
||||
"role-remove",
|
||||
"addParticipant",
|
||||
"removeParticipant",
|
||||
"renameGroup",
|
||||
"setGroupIcon",
|
||||
"leaveGroup",
|
||||
"event-create",
|
||||
"event-list",
|
||||
"timeout",
|
||||
"kick",
|
||||
"ban",
|
||||
"emoji-list",
|
||||
"emoji-upload",
|
||||
"sticker-upload",
|
||||
"voice-status",
|
||||
"download-file",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "sticker",
|
||||
actions: [
|
||||
"sticker",
|
||||
"sticker-search",
|
||||
"sticker-upload",
|
||||
"emoji-list",
|
||||
"emoji-upload",
|
||||
"download-file",
|
||||
"upload-file",
|
||||
],
|
||||
},
|
||||
{ group: "thread", actions: ["thread-create", "thread-list", "thread-reply"] },
|
||||
{ group: "event", actions: ["event-create", "event-list"] },
|
||||
{ group: "moderation", actions: ["timeout", "kick", "ban", "delete", "unsend"] },
|
||||
{
|
||||
// Keep every action that reads channel-management fields here; omission hides valid params.
|
||||
group: "channelManagement",
|
||||
actions: [
|
||||
"channel-create",
|
||||
"channel-edit",
|
||||
"channel-move",
|
||||
"category-create",
|
||||
"category-edit",
|
||||
"category-delete",
|
||||
"topic-create",
|
||||
"topic-edit",
|
||||
"renameGroup",
|
||||
"setGroupIcon",
|
||||
],
|
||||
},
|
||||
{ group: "presence", actions: ["set-presence", "set-profile", "voice-status"] },
|
||||
];
|
||||
|
||||
function isSendOnly(actions: readonly string[]): boolean {
|
||||
return actions.length > 0 && actions.every((action) => action === "send");
|
||||
}
|
||||
|
||||
function buildScopedProperties(params: {
|
||||
actions: readonly string[];
|
||||
options: MessageToolSchemaBuilderOptions;
|
||||
builders: MessageToolSchemaBuilders;
|
||||
}): SchemaProperties {
|
||||
const activeActions = new Set(params.actions);
|
||||
const properties = params.builders.base(params.options);
|
||||
for (const entry of SCOPED_ACTION_GROUPS) {
|
||||
if (entry.actions.some((action) => activeActions.has(action))) {
|
||||
Object.assign(properties, params.builders.groups[entry.group]());
|
||||
}
|
||||
}
|
||||
Object.assign(properties, params.options.extraProperties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
export function buildMessageToolSchemaFromActions(
|
||||
actions: readonly string[],
|
||||
options: MessageToolSchemaBuilderOptions,
|
||||
builders: MessageToolSchemaBuilders,
|
||||
) {
|
||||
// Keep one flat object: provider adapters reject per-action anyOf/oneOf schemas.
|
||||
// Groups prune unavailable fields; runtime still validates each action payload.
|
||||
const properties = isSendOnly(actions)
|
||||
? Object.assign(builders.base(options), options.extraProperties)
|
||||
: options.scopeToActions && actions.length > 0
|
||||
? buildScopedProperties({ actions, options, builders })
|
||||
: builders.full(options);
|
||||
return Type.Object({
|
||||
action: stringEnum(actions, {
|
||||
description:
|
||||
'Select one action. For action="send", provide message or another send payload; fields for other actions do not count as send content.',
|
||||
}),
|
||||
...properties,
|
||||
});
|
||||
}
|
||||
@@ -2119,6 +2119,118 @@ describe("message tool schema scoping", () => {
|
||||
expect(properties).not.toHaveProperty("eventName");
|
||||
});
|
||||
|
||||
it("prunes fields for action groups that discovery does not advertise", () => {
|
||||
const plugin = createChannelPlugin({
|
||||
id: "discord",
|
||||
label: "Discord",
|
||||
docsPath: "/channels/discord",
|
||||
blurb: "Discord test plugin.",
|
||||
actions: [
|
||||
"send",
|
||||
"read",
|
||||
"react",
|
||||
"reactions",
|
||||
"edit",
|
||||
"delete",
|
||||
"pin",
|
||||
"unpin",
|
||||
"list-pins",
|
||||
"thread-create",
|
||||
"thread-list",
|
||||
"thread-reply",
|
||||
"upload-file",
|
||||
],
|
||||
});
|
||||
|
||||
setActivePluginRegistry(createTestRegistry([{ pluginId: "discord", source: "test", plugin }]));
|
||||
|
||||
const tool = createMessageTool({
|
||||
config: {} as never,
|
||||
currentChannelProvider: "discord",
|
||||
});
|
||||
const properties = getToolProperties(tool);
|
||||
|
||||
expect(properties).toHaveProperty("message");
|
||||
expect(properties).toHaveProperty("messageId");
|
||||
expect(properties).toHaveProperty("threadName");
|
||||
|
||||
expect(properties).not.toHaveProperty("topic");
|
||||
expect(properties).not.toHaveProperty("rateLimitPerUser");
|
||||
expect(properties).not.toHaveProperty("clearParent");
|
||||
expect(properties).not.toHaveProperty("activityName");
|
||||
expect(properties).not.toHaveProperty("activityState");
|
||||
expect(properties).not.toHaveProperty("status");
|
||||
expect(properties).not.toHaveProperty("pollId");
|
||||
expect(properties).not.toHaveProperty("eventName");
|
||||
});
|
||||
|
||||
it.each<{
|
||||
action: ChannelMessageActionName;
|
||||
fields: string[];
|
||||
}>([
|
||||
{ action: "search", fields: ["query", "limit"] },
|
||||
{ action: "reactions", fields: ["messageId", "limit"] },
|
||||
{ action: "sticker-search", fields: ["query", "limit"] },
|
||||
{ action: "emoji-list", fields: ["guildId", "limit"] },
|
||||
{ action: "emoji-upload", fields: ["guildId", "emojiName", "media", "roleIds"] },
|
||||
{
|
||||
action: "sticker-upload",
|
||||
fields: ["guildId", "stickerName", "stickerDesc", "stickerTags", "media"],
|
||||
},
|
||||
{ action: "voice-status", fields: ["guildId", "userId"] },
|
||||
{ action: "timeout", fields: ["guildId", "userId", "durationMin", "until", "reason"] },
|
||||
{ action: "download-file", fields: ["fileId", "channelId", "threadId"] },
|
||||
{ action: "thread-create", fields: ["messageId", "threadName", "channelId"] },
|
||||
{ action: "renameGroup", fields: ["name"] },
|
||||
{ action: "setGroupIcon", fields: ["name", "filename", "buffer"] },
|
||||
{ action: "channel-info", fields: ["channelId", "pageSize", "pageToken"] },
|
||||
{ action: "channel-list", fields: ["query", "limit"] },
|
||||
])("keeps fields consumed by scoped $action handlers", ({ action, fields }) => {
|
||||
const plugin = createChannelPlugin({
|
||||
id: "test-channel",
|
||||
label: "Test Channel",
|
||||
docsPath: "/channels/test-channel",
|
||||
blurb: "Scoped schema contract plugin.",
|
||||
actions: [action],
|
||||
});
|
||||
setActivePluginRegistry(
|
||||
createTestRegistry([{ pluginId: "test-channel", source: "test", plugin }]),
|
||||
);
|
||||
|
||||
const properties = getToolProperties(
|
||||
createMessageTool({
|
||||
config: {} as never,
|
||||
currentChannelProvider: "test-channel",
|
||||
}),
|
||||
);
|
||||
|
||||
for (const field of fields) {
|
||||
expect(properties, `${action} should advertise ${field}`).toHaveProperty(field);
|
||||
}
|
||||
});
|
||||
|
||||
it("describes the send payload contract on the action and message fields", () => {
|
||||
const plugin = createChannelPlugin({
|
||||
id: "discord",
|
||||
label: "Discord",
|
||||
docsPath: "/channels/discord",
|
||||
blurb: "Discord test plugin.",
|
||||
actions: ["send", "channel-info"],
|
||||
});
|
||||
setActivePluginRegistry(createTestRegistry([{ pluginId: "discord", source: "test", plugin }]));
|
||||
|
||||
const properties = getToolProperties(
|
||||
createMessageTool({ config: {} as never, currentChannelProvider: "discord" }),
|
||||
);
|
||||
|
||||
expect((properties.action as { description?: string }).description).toContain(
|
||||
'For action="send"',
|
||||
);
|
||||
expect((properties.message as { description?: string }).description).toContain(
|
||||
"A send needs message",
|
||||
);
|
||||
});
|
||||
|
||||
it("filters scoped schemas through the per-agent message action allowlist", () => {
|
||||
const plugin = createChannelPlugin({
|
||||
id: "discord",
|
||||
@@ -2160,6 +2272,59 @@ describe("message tool schema scoping", () => {
|
||||
expect(tool.description).not.toContain("react");
|
||||
});
|
||||
|
||||
it("preserves channel-management params for scoped channel-move and category-delete allowlists", () => {
|
||||
// Regression: SCOPED_ACTION_GROUPS previously omitted channel-move and
|
||||
// category-delete from the channel-management group, so narrowing an agent
|
||||
// allowlist to either action stripped position/parentId/categoryId from
|
||||
// the schema even though the Discord handlers require them.
|
||||
const plugin = createChannelPlugin({
|
||||
id: "discord",
|
||||
label: "Discord",
|
||||
docsPath: "/channels/discord",
|
||||
blurb: "Discord test plugin.",
|
||||
actions: ["send", "channel-move", "category-delete"],
|
||||
});
|
||||
|
||||
setActivePluginRegistry(createTestRegistry([{ pluginId: "discord", source: "test", plugin }]));
|
||||
|
||||
const channelMoveTool = createMessageTool({
|
||||
config: {
|
||||
agents: {
|
||||
list: [
|
||||
{
|
||||
id: "mover",
|
||||
tools: { message: { actions: { allow: ["channel-move"] } } },
|
||||
},
|
||||
],
|
||||
},
|
||||
} as never,
|
||||
currentChannelProvider: "discord",
|
||||
agentId: "mover",
|
||||
});
|
||||
const channelMoveProps = getToolProperties(channelMoveTool);
|
||||
expect(getActionEnum(channelMoveProps)).toEqual(["channel-move"]);
|
||||
expect(channelMoveProps).toHaveProperty("position");
|
||||
expect(channelMoveProps).toHaveProperty("parentId");
|
||||
|
||||
const categoryDeleteTool = createMessageTool({
|
||||
config: {
|
||||
agents: {
|
||||
list: [
|
||||
{
|
||||
id: "purger",
|
||||
tools: { message: { actions: { allow: ["category-delete"] } } },
|
||||
},
|
||||
],
|
||||
},
|
||||
} as never,
|
||||
currentChannelProvider: "discord",
|
||||
agentId: "purger",
|
||||
});
|
||||
const categoryDeleteProps = getToolProperties(categoryDeleteTool);
|
||||
expect(getActionEnum(categoryDeleteProps)).toEqual(["category-delete"]);
|
||||
expect(categoryDeleteProps).toHaveProperty("categoryId");
|
||||
});
|
||||
|
||||
it("uses discovery account scope for other configured channel actions", () => {
|
||||
const currentPlugin = createChannelPlugin({
|
||||
id: "discord",
|
||||
|
||||
@@ -92,6 +92,12 @@ import {
|
||||
appendMessageToolReadHint,
|
||||
appendMessageToolVisibleReplyHint,
|
||||
} from "./message-tool-description.js";
|
||||
import {
|
||||
buildMessageToolQuerySchemaProperties,
|
||||
buildMessageToolSchemaFromActions,
|
||||
MESSAGE_TOOL_SEND_TEXT_DESCRIPTION,
|
||||
type MessageToolSchemaBuilders,
|
||||
} from "./message-tool-schema-scoping.js";
|
||||
import { isPollVoteEchoText } from "./poll-vote-echo.js";
|
||||
|
||||
const AllMessageActions = CHANNEL_MESSAGE_ACTION_NAMES;
|
||||
@@ -646,7 +652,7 @@ function buildSendSchema(options: {
|
||||
includeBestEffort: boolean;
|
||||
}) {
|
||||
const props: Record<string, TSchema> = {
|
||||
message: Type.Optional(Type.String()),
|
||||
message: Type.Optional(Type.String({ description: MESSAGE_TOOL_SEND_TEXT_DESCRIPTION })),
|
||||
effectId: Type.Optional(
|
||||
Type.String({
|
||||
description: "sendWithEffect id/name.",
|
||||
@@ -878,7 +884,6 @@ function buildThreadSchema() {
|
||||
|
||||
function buildEventSchema() {
|
||||
return {
|
||||
query: Type.Optional(Type.String()),
|
||||
eventName: Type.Optional(Type.String()),
|
||||
eventType: Type.Optional(Type.String()),
|
||||
startTime: Type.Optional(Type.String()),
|
||||
@@ -886,8 +891,6 @@ function buildEventSchema() {
|
||||
desc: Type.Optional(Type.String()),
|
||||
location: Type.Optional(Type.String()),
|
||||
image: Type.Optional(Type.String({ description: "Event cover image URL/path." })),
|
||||
durationMin: optionalNonNegativeIntegerSchema(),
|
||||
until: Type.Optional(Type.String()),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -895,6 +898,8 @@ function buildModerationSchema() {
|
||||
return {
|
||||
reason: Type.Optional(Type.String()),
|
||||
deleteDays: optionalNonNegativeIntegerSchema({ maximum: 7 }),
|
||||
durationMin: optionalNonNegativeIntegerSchema(),
|
||||
until: Type.Optional(Type.String()),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -964,6 +969,7 @@ function buildMessageToolSchemaProps(options: {
|
||||
...buildSendSchema(options),
|
||||
...buildReactionSchema(),
|
||||
...buildFetchSchema(),
|
||||
...buildMessageToolQuerySchemaProperties(),
|
||||
...buildPollSchema(),
|
||||
...buildChannelTargetSchema(),
|
||||
...buildStickerSchema(),
|
||||
@@ -977,48 +983,37 @@ function buildMessageToolSchemaProps(options: {
|
||||
};
|
||||
}
|
||||
|
||||
function isSendOnlyActions(actions: readonly string[]): boolean {
|
||||
const uniqueActions = new Set(actions);
|
||||
return uniqueActions.size === 1 && uniqueActions.has("send");
|
||||
}
|
||||
|
||||
function buildSendOnlyMessageToolSchemaProps(options: {
|
||||
includePresentation: boolean;
|
||||
includeDeliveryPin: boolean;
|
||||
includeBestEffort: boolean;
|
||||
extraProperties?: Record<string, TSchema>;
|
||||
}) {
|
||||
return {
|
||||
const MESSAGE_TOOL_SCHEMA_BUILDERS = {
|
||||
full: buildMessageToolSchemaProps,
|
||||
base: (options) => ({
|
||||
...buildRoutingSchema(),
|
||||
...buildSendSchema(options),
|
||||
...buildGatewaySchema(),
|
||||
...options.extraProperties,
|
||||
};
|
||||
}
|
||||
|
||||
function buildMessageToolSchemaFromActions(
|
||||
actions: readonly string[],
|
||||
options: {
|
||||
includePresentation: boolean;
|
||||
includeDeliveryPin: boolean;
|
||||
includeBestEffort: boolean;
|
||||
extraProperties?: Record<string, TSchema>;
|
||||
}),
|
||||
groups: {
|
||||
reaction: buildReactionSchema,
|
||||
fetch: buildFetchSchema,
|
||||
query: buildMessageToolQuerySchemaProperties,
|
||||
poll: buildPollSchema,
|
||||
channelTarget: buildChannelTargetSchema,
|
||||
sticker: buildStickerSchema,
|
||||
thread: buildThreadSchema,
|
||||
event: buildEventSchema,
|
||||
moderation: buildModerationSchema,
|
||||
channelManagement: buildChannelManagementSchema,
|
||||
presence: buildPresenceSchema,
|
||||
},
|
||||
) {
|
||||
const props = isSendOnlyActions(actions)
|
||||
? buildSendOnlyMessageToolSchemaProps(options)
|
||||
: buildMessageToolSchemaProps(options);
|
||||
return Type.Object({
|
||||
action: stringEnum(actions),
|
||||
...props,
|
||||
});
|
||||
}
|
||||
} satisfies MessageToolSchemaBuilders;
|
||||
|
||||
const MessageToolSchema = buildMessageToolSchemaFromActions(AllMessageActions, {
|
||||
includePresentation: true,
|
||||
includeDeliveryPin: true,
|
||||
includeBestEffort: false,
|
||||
});
|
||||
const MessageToolSchema = buildMessageToolSchemaFromActions(
|
||||
AllMessageActions,
|
||||
{
|
||||
includePresentation: true,
|
||||
includeDeliveryPin: true,
|
||||
includeBestEffort: false,
|
||||
},
|
||||
MESSAGE_TOOL_SCHEMA_BUILDERS,
|
||||
);
|
||||
|
||||
type MessageToolOptions = {
|
||||
agentAccountId?: string;
|
||||
@@ -1267,12 +1262,17 @@ function buildMessageToolSchema(params: MessageToolDiscoveryParams) {
|
||||
normalizeMessageChannel(params.currentChannelProvider) ?? undefined,
|
||||
),
|
||||
);
|
||||
return buildMessageToolSchemaFromActions(actions.length > 0 ? actions : ["send"], {
|
||||
includePresentation,
|
||||
includeDeliveryPin,
|
||||
includeBestEffort,
|
||||
extraProperties,
|
||||
});
|
||||
return buildMessageToolSchemaFromActions(
|
||||
actions.length > 0 ? actions : ["send"],
|
||||
{
|
||||
includePresentation,
|
||||
includeDeliveryPin,
|
||||
includeBestEffort,
|
||||
scopeToActions: normalizeMessageChannel(params.currentChannelProvider) !== undefined,
|
||||
extraProperties,
|
||||
},
|
||||
MESSAGE_TOOL_SCHEMA_BUILDERS,
|
||||
);
|
||||
}
|
||||
|
||||
function resolveAgentAccountId(value?: string): string | undefined {
|
||||
|
||||
+2
@@ -7,6 +7,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"action": {
|
||||
"description": "Select one action. For action=\"send\", provide message or another send payload; fields for other actions do not count as send content.",
|
||||
"enum": ["send"],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -84,6 +85,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"description": "Text for action=\"send\". A send needs message or another send payload such as media, attachments, or presentation.",
|
||||
"type": "string"
|
||||
},
|
||||
"mimeType": {
|
||||
|
||||
+2
@@ -7,6 +7,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"action": {
|
||||
"description": "Select one action. For action=\"send\", provide message or another send payload; fields for other actions do not count as send content.",
|
||||
"enum": ["send"],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -84,6 +85,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"description": "Text for action=\"send\". A send needs message or another send payload such as media, attachments, or presentation.",
|
||||
"type": "string"
|
||||
},
|
||||
"mimeType": {
|
||||
|
||||
+2
@@ -7,6 +7,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"action": {
|
||||
"description": "Select one action. For action=\"send\", provide message or another send payload; fields for other actions do not count as send content.",
|
||||
"enum": ["send"],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -84,6 +85,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"description": "Text for action=\"send\". A send needs message or another send payload such as media, attachments, or presentation.",
|
||||
"type": "string"
|
||||
},
|
||||
"mimeType": {
|
||||
|
||||
Vendored
+6
-4
@@ -208,8 +208,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 0
|
||||
},
|
||||
"dynamicToolsJson": {
|
||||
"chars": 52932,
|
||||
"roughTokens": 13233
|
||||
"chars": 53240,
|
||||
"roughTokens": 13310
|
||||
},
|
||||
"openClawDeveloperInstructions": {
|
||||
"chars": 3431,
|
||||
@@ -220,8 +220,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 6989
|
||||
},
|
||||
"totalWithDynamicToolsJson": {
|
||||
"chars": 80890,
|
||||
"roughTokens": 20223
|
||||
"chars": 81198,
|
||||
"roughTokens": 20300
|
||||
},
|
||||
"userInputText": {
|
||||
"chars": 1442,
|
||||
@@ -561,6 +561,7 @@ Full JSON: `codex-dynamic-tools.discord-group.json`
|
||||
"type": "string"
|
||||
},
|
||||
"action": {
|
||||
"description": "Select one action. For action=\"send\", provide message or another send payload; fields for other actions do not count as send content.",
|
||||
"enum": ["send"],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -638,6 +639,7 @@ Full JSON: `codex-dynamic-tools.discord-group.json`
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"description": "Text for action=\"send\". A send needs message or another send payload such as media, attachments, or presentation.",
|
||||
"type": "string"
|
||||
},
|
||||
"mimeType": {
|
||||
|
||||
test/fixtures/agents/prompt-snapshots/codex-runtime-happy-path/telegram-direct-codex-message-tool.md
Vendored
+6
-4
@@ -208,8 +208,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 0
|
||||
},
|
||||
"dynamicToolsJson": {
|
||||
"chars": 52659,
|
||||
"roughTokens": 13165
|
||||
"chars": 52967,
|
||||
"roughTokens": 13242
|
||||
},
|
||||
"openClawDeveloperInstructions": {
|
||||
"chars": 2322,
|
||||
@@ -220,8 +220,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 6610
|
||||
},
|
||||
"totalWithDynamicToolsJson": {
|
||||
"chars": 79099,
|
||||
"roughTokens": 19775
|
||||
"chars": 79407,
|
||||
"roughTokens": 19852
|
||||
},
|
||||
"userInputText": {
|
||||
"chars": 1033,
|
||||
@@ -548,6 +548,7 @@ Full JSON: `codex-dynamic-tools.telegram-direct.json`
|
||||
"type": "string"
|
||||
},
|
||||
"action": {
|
||||
"description": "Select one action. For action=\"send\", provide message or another send payload; fields for other actions do not count as send content.",
|
||||
"enum": ["send"],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -625,6 +626,7 @@ Full JSON: `codex-dynamic-tools.telegram-direct.json`
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"description": "Text for action=\"send\". A send needs message or another send payload such as media, attachments, or presentation.",
|
||||
"type": "string"
|
||||
},
|
||||
"mimeType": {
|
||||
|
||||
Vendored
+6
-4
@@ -209,8 +209,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 0
|
||||
},
|
||||
"dynamicToolsJson": {
|
||||
"chars": 53949,
|
||||
"roughTokens": 13488
|
||||
"chars": 54257,
|
||||
"roughTokens": 13565
|
||||
},
|
||||
"openClawDeveloperInstructions": {
|
||||
"chars": 2341,
|
||||
@@ -221,8 +221,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 6745
|
||||
},
|
||||
"totalWithDynamicToolsJson": {
|
||||
"chars": 80928,
|
||||
"roughTokens": 20232
|
||||
"chars": 81236,
|
||||
"roughTokens": 20309
|
||||
},
|
||||
"userInputText": {
|
||||
"chars": 1271,
|
||||
@@ -556,6 +556,7 @@ Full JSON: `codex-dynamic-tools.heartbeat-turn.json`
|
||||
"type": "string"
|
||||
},
|
||||
"action": {
|
||||
"description": "Select one action. For action=\"send\", provide message or another send payload; fields for other actions do not count as send content.",
|
||||
"enum": ["send"],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -633,6 +634,7 @@ Full JSON: `codex-dynamic-tools.heartbeat-turn.json`
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"description": "Text for action=\"send\". A send needs message or another send payload such as media, attachments, or presentation.",
|
||||
"type": "string"
|
||||
},
|
||||
"mimeType": {
|
||||
|
||||
Reference in New Issue
Block a user