@@ -19,33 +19,20 @@ import {
1919} from "openclaw/plugin-sdk/agent-harness" ;
2020import { handleCodexAppServerApprovalRequest } from "./approval-bridge.js" ;
2121import { isCodexAppServerApprovalRequest , type CodexAppServerClient } from "./client.js" ;
22- import {
23- resolveCodexAppServerRuntimeOptions ,
24- type CodexAppServerRuntimeOptions ,
25- type CodexAppServerStartOptions ,
26- } from "./config.js" ;
22+ import { resolveCodexAppServerRuntimeOptions , type CodexAppServerStartOptions } from "./config.js" ;
2723import { createCodexDynamicToolBridge } from "./dynamic-tools.js" ;
2824import { CodexAppServerEventProjector } from "./event-projector.js" ;
2925import {
3026 isJsonObject ,
3127 type CodexServerNotification ,
3228 type CodexDynamicToolCallParams ,
33- type CodexThreadResumeParams ,
34- type CodexThreadResumeResponse ,
35- type CodexThreadStartResponse ,
36- type CodexTurnStartParams ,
3729 type CodexTurnStartResponse ,
38- type CodexUserInput ,
3930 type JsonObject ,
4031 type JsonValue ,
4132} from "./protocol.js" ;
42- import {
43- clearCodexAppServerBinding ,
44- readCodexAppServerBinding ,
45- writeCodexAppServerBinding ,
46- type CodexAppServerThreadBinding ,
47- } from "./session-binding.js" ;
33+ import type { CodexAppServerThreadBinding } from "./session-binding.js" ;
4834import { clearSharedCodexAppServerClient , getSharedCodexAppServerClient } from "./shared-client.js" ;
35+ import { buildTurnStartParams , startOrResumeThread } from "./thread-lifecycle.js" ;
4936import { mirrorCodexAppServerTranscript } from "./transcript-mirror.js" ;
5037
5138type CodexAppServerClientFactory = (
@@ -397,199 +384,6 @@ async function withCodexStartupTimeout<T>(params: {
397384 }
398385}
399386
400- async function startOrResumeThread ( params : {
401- client : CodexAppServerClient ;
402- params : EmbeddedRunAttemptParams ;
403- cwd : string ;
404- dynamicTools : JsonValue [ ] ;
405- appServer : CodexAppServerRuntimeOptions ;
406- } ) : Promise < CodexAppServerThreadBinding > {
407- const dynamicToolsFingerprint = fingerprintDynamicTools ( params . dynamicTools ) ;
408- const binding = await readCodexAppServerBinding ( params . params . sessionFile ) ;
409- if ( binding ?. threadId ) {
410- // `/codex resume <thread>` writes a binding before the next turn can know
411- // the dynamic tool catalog, so only invalidate fingerprints we actually have.
412- if (
413- binding . dynamicToolsFingerprint &&
414- binding . dynamicToolsFingerprint !== dynamicToolsFingerprint
415- ) {
416- embeddedAgentLog . debug (
417- "codex app-server dynamic tool catalog changed; starting a new thread" ,
418- {
419- threadId : binding . threadId ,
420- } ,
421- ) ;
422- await clearCodexAppServerBinding ( params . params . sessionFile ) ;
423- } else {
424- try {
425- const response = await params . client . request < CodexThreadResumeResponse > (
426- "thread/resume" ,
427- buildThreadResumeParams ( params . params , {
428- threadId : binding . threadId ,
429- appServer : params . appServer ,
430- } ) ,
431- ) ;
432- await writeCodexAppServerBinding ( params . params . sessionFile , {
433- threadId : response . thread . id ,
434- cwd : params . cwd ,
435- model : params . params . modelId ,
436- modelProvider : response . modelProvider ?? normalizeModelProvider ( params . params . provider ) ,
437- dynamicToolsFingerprint,
438- createdAt : binding . createdAt ,
439- } ) ;
440- return {
441- ...binding ,
442- threadId : response . thread . id ,
443- cwd : params . cwd ,
444- model : params . params . modelId ,
445- modelProvider : response . modelProvider ?? normalizeModelProvider ( params . params . provider ) ,
446- dynamicToolsFingerprint,
447- } ;
448- } catch ( error ) {
449- embeddedAgentLog . warn ( "codex app-server thread resume failed; starting a new thread" , {
450- error,
451- } ) ;
452- await clearCodexAppServerBinding ( params . params . sessionFile ) ;
453- }
454- }
455- }
456-
457- const response = await params . client . request < CodexThreadStartResponse > ( "thread/start" , {
458- model : params . params . modelId ,
459- modelProvider : normalizeModelProvider ( params . params . provider ) ,
460- cwd : params . cwd ,
461- approvalPolicy : params . appServer . approvalPolicy ,
462- approvalsReviewer : params . appServer . approvalsReviewer ,
463- sandbox : params . appServer . sandbox ,
464- ...( params . appServer . serviceTier ? { serviceTier : params . appServer . serviceTier } : { } ) ,
465- serviceName : "OpenClaw" ,
466- developerInstructions : buildDeveloperInstructions ( params . params ) ,
467- dynamicTools : params . dynamicTools ,
468- experimentalRawEvents : true ,
469- persistExtendedHistory : true ,
470- } ) ;
471- const createdAt = new Date ( ) . toISOString ( ) ;
472- await writeCodexAppServerBinding ( params . params . sessionFile , {
473- threadId : response . thread . id ,
474- cwd : params . cwd ,
475- model : response . model ?? params . params . modelId ,
476- modelProvider : response . modelProvider ?? normalizeModelProvider ( params . params . provider ) ,
477- dynamicToolsFingerprint,
478- createdAt,
479- } ) ;
480- return {
481- schemaVersion : 1 ,
482- threadId : response . thread . id ,
483- sessionFile : params . params . sessionFile ,
484- cwd : params . cwd ,
485- model : response . model ?? params . params . modelId ,
486- modelProvider : response . modelProvider ?? normalizeModelProvider ( params . params . provider ) ,
487- dynamicToolsFingerprint,
488- createdAt,
489- updatedAt : createdAt ,
490- } ;
491- }
492-
493- export function buildThreadResumeParams (
494- params : EmbeddedRunAttemptParams ,
495- options : {
496- threadId : string ;
497- appServer : CodexAppServerRuntimeOptions ;
498- } ,
499- ) : CodexThreadResumeParams {
500- return {
501- threadId : options . threadId ,
502- model : params . modelId ,
503- modelProvider : normalizeModelProvider ( params . provider ) ,
504- approvalPolicy : options . appServer . approvalPolicy ,
505- approvalsReviewer : options . appServer . approvalsReviewer ,
506- sandbox : options . appServer . sandbox ,
507- ...( options . appServer . serviceTier ? { serviceTier : options . appServer . serviceTier } : { } ) ,
508- persistExtendedHistory : true ,
509- } ;
510- }
511-
512- export function buildTurnStartParams (
513- params : EmbeddedRunAttemptParams ,
514- options : {
515- threadId : string ;
516- cwd : string ;
517- appServer : CodexAppServerRuntimeOptions ;
518- } ,
519- ) : CodexTurnStartParams {
520- return {
521- threadId : options . threadId ,
522- input : buildUserInput ( params ) ,
523- cwd : options . cwd ,
524- approvalPolicy : options . appServer . approvalPolicy ,
525- approvalsReviewer : options . appServer . approvalsReviewer ,
526- model : params . modelId ,
527- ...( options . appServer . serviceTier ? { serviceTier : options . appServer . serviceTier } : { } ) ,
528- effort : resolveReasoningEffort ( params . thinkLevel ) ,
529- } ;
530- }
531-
532- function fingerprintDynamicTools ( dynamicTools : JsonValue [ ] ) : string {
533- return JSON . stringify ( dynamicTools . map ( stabilizeJsonValue ) ) ;
534- }
535-
536- function stabilizeJsonValue ( value : JsonValue ) : JsonValue {
537- if ( Array . isArray ( value ) ) {
538- return value . map ( stabilizeJsonValue ) ;
539- }
540- if ( ! isJsonObject ( value ) ) {
541- return value ;
542- }
543- const stable : JsonObject = { } ;
544- for ( const [ key , child ] of Object . entries ( value ) . toSorted ( ( [ left ] , [ right ] ) =>
545- left . localeCompare ( right ) ,
546- ) ) {
547- stable [ key ] = stabilizeJsonValue ( child ) ;
548- }
549- return stable ;
550- }
551-
552- function buildDeveloperInstructions ( params : EmbeddedRunAttemptParams ) : string {
553- const sections = [
554- "You are running inside OpenClaw. Use OpenClaw dynamic tools for messaging, cron, sessions, and host actions when available." ,
555- "Preserve the user's existing channel/session context. If sending a channel reply, use the OpenClaw messaging tool instead of describing that you would reply." ,
556- params . extraSystemPrompt ,
557- params . skillsSnapshot ?. prompt ,
558- ] ;
559- return sections . filter ( ( section ) => typeof section === "string" && section . trim ( ) ) . join ( "\n\n" ) ;
560- }
561-
562- function buildUserInput ( params : EmbeddedRunAttemptParams ) : CodexUserInput [ ] {
563- return [
564- { type : "text" , text : params . prompt } ,
565- ...( params . images ?? [ ] ) . map (
566- ( image ) : CodexUserInput => ( {
567- type : "image" ,
568- url : `data:${ image . mimeType } ;base64,${ image . data } ` ,
569- } ) ,
570- ) ,
571- ] ;
572- }
573-
574- function normalizeModelProvider ( provider : string ) : string {
575- return provider === "codex" || provider === "openai-codex" ? "openai" : provider ;
576- }
577-
578- function resolveReasoningEffort (
579- thinkLevel : EmbeddedRunAttemptParams [ "thinkLevel" ] ,
580- ) : "minimal" | "low" | "medium" | "high" | "xhigh" | null {
581- if (
582- thinkLevel === "minimal" ||
583- thinkLevel === "low" ||
584- thinkLevel === "medium" ||
585- thinkLevel === "high" ||
586- thinkLevel === "xhigh"
587- ) {
588- return thinkLevel ;
589- }
590- return null ;
591- }
592-
593387function readDynamicToolCallParams (
594388 value : JsonValue | undefined ,
595389) : CodexDynamicToolCallParams | undefined {
0 commit comments