Skip to content

Commit 9a00d74

Browse files
committed
fix: share skill workshop prompt with codex
1 parent e9d0132 commit 9a00d74

6 files changed

Lines changed: 68 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Docs: https://docs.openclaw.ai
3030

3131
### Fixes
3232

33+
- Agents/Codex: surface Skill Workshop guidance in Codex app-server prompts when `skill_workshop` is available. Thanks @shakkernerd.
3334
- CLI: keep `plugins list --json` on the snapshot-only path so plugin sweeps avoid loading the full runtime status graph.
3435
- Plugins: make PixVerse external-plugin ClawHub metadata explicit and keep it out of bundled dist builds.
3536
- Cron: keep SQLite cron migrations compatible with legacy run-log tables, archived job stores, diagnostic cron names, and legacy one-shot delete-after-run behavior. (#88285)

extensions/codex/src/app-server/thread-lifecycle.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,28 @@ describe("Codex app-server native code mode config", () => {
103103
expect(instructions).not.toContain("message,");
104104
});
105105

106+
it("uses the shared Skill Workshop guidance when skill_workshop is available", () => {
107+
const instructions = buildDeveloperInstructions(createAttemptParams({ provider: "openai" }), {
108+
dynamicTools: [
109+
{
110+
name: "skill_workshop",
111+
description: "Manage skill proposals",
112+
inputSchema: { type: "object" },
113+
namespace: "openclaw",
114+
deferLoading: true,
115+
},
116+
],
117+
});
118+
119+
expect(instructions).toContain("## Skill Workshop");
120+
expect(instructions).toContain(
121+
"Use `skill_workshop` when the user wants to create, update, revise, list, inspect, apply, reject, or quarantine a reusable skill, Skill Workshop proposal, playbook, workflow, procedure, or durable instruction.",
122+
);
123+
expect(instructions).toContain(
124+
"Use `action=apply`, `action=reject`, or `action=quarantine` only after the user explicitly asks to approve/use/apply, reject, or quarantine a specific proposal.",
125+
);
126+
});
127+
106128
it("keeps developer instructions compact when no dynamic tools are deferred", () => {
107129
const instructions = buildDeveloperInstructions(createAttemptParams({ provider: "openai" }), {
108130
dynamicTools: [

extensions/codex/src/app-server/thread-lifecycle.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import {
2+
buildSkillWorkshopPromptSection,
23
embeddedAgentLog,
34
formatErrorMessage,
45
isActiveHarnessContextEngine,
6+
SKILL_WORKSHOP_TOOL_NAME,
57
type EmbeddedRunAttemptParams,
68
} from "openclaw/plugin-sdk/agent-harness-runtime";
79
import { buildCodexUserMcpServersThreadConfigPatch } from "openclaw/plugin-sdk/codex-mcp-projection";
@@ -1151,6 +1153,7 @@ export function buildDeveloperInstructions(
11511153
const sections = [
11521154
"You are a personal agent running inside OpenClaw. OpenClaw has dynamic tools for OpenClaw-owned messaging, cron, sessions, media, gateway, and nodes.",
11531155
buildDeferredDynamicToolManifest(options.dynamicTools),
1156+
buildSkillWorkshopInstruction(options.dynamicTools),
11541157
"Use Codex native `spawn_agent` for Codex subagents. Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation.",
11551158
buildVisibleReplyInstruction(params, options.dynamicTools),
11561159
nativeCommandGuidance,
@@ -1176,6 +1179,18 @@ function buildDeferredDynamicToolManifest(
11761179
return `Deferred searchable OpenClaw dynamic tools available: ${deferredToolNames.join(", ")}. Use \`tool_search\` to load exact callable specs before use.`;
11771180
}
11781181

1182+
function buildSkillWorkshopInstruction(
1183+
dynamicTools: readonly CodexDynamicToolSpec[] | undefined,
1184+
): string | undefined {
1185+
const hasSkillWorkshop = (dynamicTools ?? []).some(
1186+
(tool) => tool.name.trim() === SKILL_WORKSHOP_TOOL_NAME,
1187+
);
1188+
if (!hasSkillWorkshop) {
1189+
return undefined;
1190+
}
1191+
return buildSkillWorkshopPromptSection().join("\n");
1192+
}
1193+
11791194
function buildVisibleReplyInstruction(
11801195
params: EmbeddedRunAttemptParams,
11811196
dynamicTools: readonly CodexDynamicToolSpec[] | undefined,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
export const SKILL_WORKSHOP_TOOL_NAME = "skill_workshop";
2+
3+
export function buildSkillWorkshopPromptSection(): string[] {
4+
return [
5+
"## Skill Workshop",
6+
"Use `skill_workshop` when the user wants to create, update, revise, list, inspect, apply, reject, or quarantine a reusable skill, Skill Workshop proposal, playbook, workflow, procedure, or durable instruction.",
7+
"Treat a request as durable when it should be saved, repeated, proposed, installed later, shared as a skill, or used as a standing workflow instead of answered once in chat.",
8+
"Do not create or change skill proposal files manually with `write`, `edit`, `exec`, shell commands, or direct filesystem operations. The final proposal artifact must go through `skill_workshop`.",
9+
"Use `action=create` for a new skill, `action=update` for an existing approved/live skill, and `action=revise` for an existing pending proposal; keep `description` under 160 bytes and `proposal_content` within the configured body limit.",
10+
"For `action=update`, pass a concise `description` when the existing live skill description should be shortened in the proposal listing.",
11+
"For `action=revise`, pass `proposal_id` when known. If it is not known, pass the proposal or skill name in `name` so `skill_workshop` can resolve the pending proposal or return candidates.",
12+
"Use `action=list` or `action=inspect` only for pending proposal discovery/inspection. Do not use filesystem search for proposal discovery.",
13+
"If the user names an existing live skill, read or view that skill when needed for context, but create the update proposal through `skill_workshop`.",
14+
"Generated skills are pending proposals by default. Do not apply, install, approve, enable, or write into live skills unless the user explicitly asks for that separate action.",
15+
"Use `action=apply`, `action=reject`, or `action=quarantine` only after the user explicitly asks to approve/use/apply, reject, or quarantine a specific proposal. Pass `proposal_id`; if it is not known, use `action=list` or `action=inspect` first.",
16+
"Do not apply, reject, or quarantine proposals manually with filesystem operations or shell commands. Proposal lifecycle changes must use `skill_workshop`.",
17+
"You may gather context first, but the durable proposal write or lifecycle change must use `skill_workshop`.",
18+
"",
19+
];
20+
}

src/agents/system-prompt.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ import {
4141
shouldRenderOpenClawToolWorkflowHints,
4242
} from "./prompt-surface.js";
4343
import { sanitizeForPromptLiteral } from "./sanitize-for-prompt.js";
44+
import {
45+
buildSkillWorkshopPromptSection,
46+
SKILL_WORKSHOP_TOOL_NAME,
47+
} from "./skill-workshop-prompt.js";
4448
import { SYSTEM_PROMPT_CACHE_BOUNDARY } from "./system-prompt-cache-boundary.js";
4549
import type {
4650
ProviderSystemPromptContribution,
@@ -923,23 +927,8 @@ export function buildAgentSystemPrompt(params: {
923927
skillsPrompt,
924928
readToolName,
925929
});
926-
const skillWorkshopSection = availableTools.has("skill_workshop")
927-
? [
928-
"## Skill Workshop",
929-
"Use `skill_workshop` when the user wants to create, update, revise, list, inspect, apply, reject, or quarantine a reusable skill, Skill Workshop proposal, playbook, workflow, procedure, or durable instruction.",
930-
"Treat a request as durable when it should be saved, repeated, proposed, installed later, shared as a skill, or used as a standing workflow instead of answered once in chat.",
931-
"Do not create or change skill proposal files manually with `write`, `edit`, `exec`, shell commands, or direct filesystem operations. The final proposal artifact must go through `skill_workshop`.",
932-
"Use `action=create` for a new skill, `action=update` for an existing approved/live skill, and `action=revise` for an existing pending proposal; keep `description` under 160 bytes and `proposal_content` within the configured body limit.",
933-
"For `action=update`, pass a concise `description` when the existing live skill description should be shortened in the proposal listing.",
934-
"For `action=revise`, pass `proposal_id` when known. If it is not known, pass the proposal or skill name in `name` so `skill_workshop` can resolve the pending proposal or return candidates.",
935-
"Use `action=list` or `action=inspect` only for pending proposal discovery/inspection. Do not use filesystem search for proposal discovery.",
936-
"If the user names an existing live skill, read or view that skill when needed for context, but create the update proposal through `skill_workshop`.",
937-
"Generated skills are pending proposals by default. Do not apply, install, approve, enable, or write into live skills unless the user explicitly asks for that separate action.",
938-
"Use `action=apply`, `action=reject`, or `action=quarantine` only after the user explicitly asks to approve/use/apply, reject, or quarantine a specific proposal. Pass `proposal_id`; if it is not known, use `action=list` or `action=inspect` first.",
939-
"Do not apply, reject, or quarantine proposals manually with filesystem operations or shell commands. Proposal lifecycle changes must use `skill_workshop`.",
940-
"You may gather context first, but the durable proposal write or lifecycle change must use `skill_workshop`.",
941-
"",
942-
]
930+
const skillWorkshopSection = availableTools.has(SKILL_WORKSHOP_TOOL_NAME)
931+
? buildSkillWorkshopPromptSection()
943932
: [];
944933
const memorySection = buildMemorySection({
945934
isMinimal,

src/plugin-sdk/agent-harness-runtime.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ export {
133133
} from "../agents/agent-scope.js";
134134
export { resolveModelAuthMode } from "../agents/model-auth.js";
135135
export { supportsModelTools } from "../agents/model-tool-support.js";
136+
export {
137+
buildSkillWorkshopPromptSection,
138+
SKILL_WORKSHOP_TOOL_NAME,
139+
} from "../agents/skill-workshop-prompt.js";
136140
export { resolveAttemptFsWorkspaceOnly } from "../agents/embedded-agent-runner/run/attempt.prompt-helpers.js";
137141
export { resolveAttemptSpawnWorkspaceDir } from "../agents/embedded-agent-runner/run/attempt.thread-helpers.js";
138142
export { buildEmbeddedAttemptToolRunContext } from "../agents/embedded-agent-runner/run/attempt.tool-run-context.js";

0 commit comments

Comments
 (0)