mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
4b0151682e
Remove hidden follow-up extraction, heartbeat delivery, CLI, docs, and supporting tests/tooling. Existing commitment records remain inert pending separately approved cleanup. Co-authored-by: Ayaan Zaidi <hi@obviy.us>
186 lines
8.1 KiB
TypeScript
186 lines
8.1 KiB
TypeScript
import type { EmbeddedRunAttemptParams } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
import { GPT5_HEARTBEAT_PROMPT_OVERLAY as CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY } from "openclaw/plugin-sdk/provider-model-shared";
|
|
import { codexSandboxPolicyForTurn, type CodexAppServerRuntimeOptions } from "./config.js";
|
|
import type {
|
|
CodexSandboxPolicy,
|
|
CodexTurnEnvironmentParams,
|
|
CodexTurnStartParams,
|
|
} from "./protocol.js";
|
|
import { readCodexSupportedReasoningEfforts } from "./reasoning-effort.js";
|
|
import {
|
|
CODEX_NATIVE_PERSONALITY_NONE,
|
|
resolveCodexAppServerRequestModelSelection,
|
|
resolveReasoningEffort,
|
|
} from "./thread-model-selection.js";
|
|
import { buildCodexUserInput } from "./user-input.js";
|
|
|
|
export function buildTurnStartParams(
|
|
params: EmbeddedRunAttemptParams,
|
|
options: {
|
|
threadId: string;
|
|
cwd: string;
|
|
appServer: CodexAppServerRuntimeOptions;
|
|
promptText?: string;
|
|
sandboxPolicy?: CodexSandboxPolicy;
|
|
environmentSelection?: CodexTurnEnvironmentParams[];
|
|
model?: string | null;
|
|
modelProvider?: string | null;
|
|
turnScopedDeveloperInstructions?: string;
|
|
skillsCollaborationInstructions?: string;
|
|
memoryCollaborationInstructions?: string;
|
|
preserveNativeTurnSettings?: boolean;
|
|
clearInheritedServiceTier?: boolean;
|
|
},
|
|
): CodexTurnStartParams {
|
|
const modelSelection = options.preserveNativeTurnSettings
|
|
? undefined
|
|
: resolveCodexAppServerRequestModelSelection({
|
|
model: options.model ?? params.modelId,
|
|
modelProvider: options.modelProvider,
|
|
authProfileId: params.authProfileId,
|
|
authProfileStore: params.authProfileStore,
|
|
agentDir: params.agentDir,
|
|
config: params.config,
|
|
});
|
|
const useThreadPermissionProfile = options.appServer.networkProxy && !options.sandboxPolicy;
|
|
return {
|
|
threadId: options.threadId,
|
|
input: buildCodexUserInput(options.promptText ?? params.prompt, params.images),
|
|
cwd: options.cwd,
|
|
approvalPolicy: options.appServer.approvalPolicy,
|
|
approvalsReviewer: options.appServer.approvalsReviewer,
|
|
...(useThreadPermissionProfile
|
|
? {}
|
|
: {
|
|
sandboxPolicy:
|
|
options.sandboxPolicy ??
|
|
codexSandboxPolicyForTurn(
|
|
options.appServer.sandbox,
|
|
options.cwd,
|
|
options.appServer.start?.args,
|
|
),
|
|
}),
|
|
...(modelSelection
|
|
? { model: modelSelection.model, personality: CODEX_NATIVE_PERSONALITY_NONE }
|
|
: {}),
|
|
// Codex distinguishes an omitted native default from explicitly clearing
|
|
// an OpenClaw-owned priority override left on this exact warm session.
|
|
...(options.appServer.serviceTier !== undefined
|
|
? { serviceTier: options.appServer.serviceTier }
|
|
: options.clearInheritedServiceTier
|
|
? { serviceTier: null }
|
|
: {}),
|
|
...(modelSelection
|
|
? {
|
|
effort: resolveReasoningEffort(
|
|
params.thinkLevel,
|
|
modelSelection.model,
|
|
readCodexSupportedReasoningEfforts(params.model?.compat),
|
|
),
|
|
}
|
|
: {}),
|
|
...(options.environmentSelection ? { environments: options.environmentSelection } : {}),
|
|
...(modelSelection
|
|
? {
|
|
collaborationMode: buildTurnCollaborationMode(params, {
|
|
model: modelSelection.model,
|
|
turnScopedDeveloperInstructions: options.turnScopedDeveloperInstructions,
|
|
skillsCollaborationInstructions: options.skillsCollaborationInstructions,
|
|
memoryCollaborationInstructions: options.memoryCollaborationInstructions,
|
|
}),
|
|
}
|
|
: {}),
|
|
};
|
|
}
|
|
|
|
type CodexTurnCollaborationMode = NonNullable<CodexTurnStartParams["collaborationMode"]>;
|
|
|
|
export function buildTurnCollaborationMode(
|
|
params: EmbeddedRunAttemptParams,
|
|
options: {
|
|
model?: string;
|
|
turnScopedDeveloperInstructions?: string;
|
|
skillsCollaborationInstructions?: string;
|
|
memoryCollaborationInstructions?: string;
|
|
} = {},
|
|
): CodexTurnCollaborationMode {
|
|
const model = options.model ?? params.modelId;
|
|
return {
|
|
mode: "default",
|
|
settings: {
|
|
model,
|
|
reasoning_effort: resolveReasoningEffort(
|
|
params.thinkLevel,
|
|
model,
|
|
readCodexSupportedReasoningEfforts(params.model?.compat),
|
|
),
|
|
developer_instructions: buildTurnScopedCollaborationInstructions(params, options),
|
|
},
|
|
};
|
|
}
|
|
|
|
function buildTurnScopedCollaborationInstructions(
|
|
params: EmbeddedRunAttemptParams,
|
|
options: {
|
|
turnScopedDeveloperInstructions?: string;
|
|
skillsCollaborationInstructions?: string;
|
|
memoryCollaborationInstructions?: string;
|
|
} = {},
|
|
): string | null {
|
|
const contextInstructions = joinPresentSections(
|
|
options.turnScopedDeveloperInstructions,
|
|
options.memoryCollaborationInstructions,
|
|
options.skillsCollaborationInstructions,
|
|
);
|
|
if (params.trigger === "cron") {
|
|
return joinPresentSections(buildCronCollaborationInstructions(), contextInstructions);
|
|
}
|
|
if (params.trigger === "heartbeat") {
|
|
return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions);
|
|
}
|
|
if (contextInstructions?.trim()) {
|
|
return joinPresentSections(buildDefaultCollaborationInstructions(), contextInstructions);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function buildDefaultCollaborationInstructions(): string {
|
|
// Codex only applies the built-in Default-mode preset when `developer_instructions`
|
|
// is null. OpenClaw adds per-turn workspace instructions here, so preserve that
|
|
// pinned Codex default behavior before appending the workspace overlay.
|
|
return [
|
|
"# Collaboration Mode: Default",
|
|
"",
|
|
"You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active.",
|
|
"",
|
|
"Your active mode changes only when new developer instructions with a different `<collaboration_mode>...</collaboration_mode>` change it; user requests or tool descriptions do not change mode by themselves. Known mode names are Default and Plan.",
|
|
"",
|
|
"## request_user_input availability",
|
|
"",
|
|
"Use the `request_user_input` tool only when it is listed in the available tools for this turn.",
|
|
"",
|
|
"In Default mode, strongly prefer making reasonable assumptions and executing the user's request rather than stopping to ask questions. If you absolutely must ask a question because the answer cannot be discovered from local context and a reasonable assumption would be risky, ask the user directly with a concise plain-text question. Never write a multiple choice question as a textual assistant message.",
|
|
].join("\n");
|
|
}
|
|
|
|
function buildCronCollaborationInstructions(): string {
|
|
return [
|
|
"This is an OpenClaw cron automation turn. Apply these instructions only to this scheduled job; ordinary chat turns should stay in Codex Default mode.",
|
|
"Execute the cron payload directly. If it asks you to run an exact command, run that command before doing any investigation, planning, memory review, or workspace bootstrap.",
|
|
"Use context already provided by the runtime, but do not spend time loading or re-reading workspace bootstrap, memory, or project-doc files before executing the cron payload. Inspect those files only if the payload asks for them or the command fails and they are needed to diagnose it.",
|
|
"Keep output concise and automation-oriented. Prefer the final command result or a short failure summary over status narration.",
|
|
].join("\n\n");
|
|
}
|
|
|
|
function buildHeartbeatCollaborationInstructions(): string {
|
|
return [
|
|
"This is an OpenClaw heartbeat turn. Apply these instructions only to this heartbeat wake; ordinary chat turns should stay in Codex Default mode.",
|
|
"When you are ready to end the heartbeat, prefer the structured `heartbeat_respond` tool so OpenClaw can record the wake outcome and notification decision. If `heartbeat_respond` is not already available and `tool_search` is available, search for `heartbeat_respond`, load it, then call it. Use `notify=false` when nothing should visibly interrupt the user.",
|
|
CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY,
|
|
].join("\n\n");
|
|
}
|
|
|
|
function joinPresentSections(...sections: Array<string | undefined>): string {
|
|
return sections.filter((section): section is string => Boolean(section?.trim())).join("\n\n");
|
|
}
|