mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 18:30:39 +09:00
[codex] Improve transient recovery and Codex model refresh (#4383)
## Thinking Path > - Paperclip orchestrates AI agents for zero-human companies > - Adapter execution and retry classification decide whether agent work pauses, retries, or recovers automatically > - Transient provider failures need to be classified precisely so Paperclip does not convert retryable upstream conditions into false hard failures > - At the same time, operators need an up-to-date model list for Codex-backed agents and prompts should nudge agents toward targeted verification instead of repo-wide sweeps > - This pull request tightens transient recovery classification for Claude and Codex, updates the agent prompt guidance, and adds Codex model refresh support end-to-end > - The benefit is better automatic retry behavior plus fresher operator-facing model configuration ## What Changed - added Codex usage-limit retry-window parsing and Claude extra-usage transient classification - normalized the heartbeat transient-recovery contract across adapter executions and heartbeat scheduling - documented that deferred comment wakes only reopen completed issues for human/comment-reopen interactions, while system follow-ups leave closed work closed - updated adapter-utils prompt guidance to prefer targeted verification - added Codex model refresh support in the server route, registry, shared types, and agent config form - added adapter/server tests covering the new parsing, retry scheduling, and model-refresh behavior ## Verification - `pnpm exec vitest run --project @paperclipai/adapter-utils packages/adapter-utils/src/server-utils.test.ts` - `pnpm exec vitest run --project @paperclipai/adapter-claude-local packages/adapters/claude-local/src/server/parse.test.ts` - `pnpm exec vitest run --project @paperclipai/adapter-codex-local packages/adapters/codex-local/src/server/parse.test.ts` - `pnpm exec vitest run --project @paperclipai/server server/src/__tests__/adapter-model-refresh-routes.test.ts server/src/__tests__/adapter-models.test.ts server/src/__tests__/claude-local-execute.test.ts server/src/__tests__/codex-local-execute.test.ts server/src/__tests__/heartbeat-process-recovery.test.ts server/src/__tests__/heartbeat-retry-scheduling.test.ts` ## Risks - Moderate behavior risk: retry classification affects whether runs auto-recover or block, so mistakes here could either suppress needed retries or over-retry real failures - Low workflow risk: deferred comment wake reopening is intentionally scoped to human/comment-reopen interactions so system follow-ups do not revive completed issues unexpectedly > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex GPT-5-based coding agent with tool use and code execution in the Codex CLI environment ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] If this change affects the UI, I have included before/after screenshots - [ ] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
4fdbbeced3
commit
8f1cd0474f
25 changed files with 1455 additions and 48 deletions
|
|
@ -70,14 +70,15 @@ async function fetchOpenAiModels(apiKey: string): Promise<AdapterModel[]> {
|
|||
}
|
||||
}
|
||||
|
||||
export async function listCodexModels(): Promise<AdapterModel[]> {
|
||||
async function loadCodexModels(options?: { forceRefresh?: boolean }): Promise<AdapterModel[]> {
|
||||
const forceRefresh = options?.forceRefresh === true;
|
||||
const apiKey = resolveOpenAiApiKey();
|
||||
const fallback = dedupeModels(codexFallbackModels);
|
||||
if (!apiKey) return fallback;
|
||||
|
||||
const now = Date.now();
|
||||
const keyFingerprint = fingerprint(apiKey);
|
||||
if (cached && cached.keyFingerprint === keyFingerprint && cached.expiresAt > now) {
|
||||
if (!forceRefresh && cached && cached.keyFingerprint === keyFingerprint && cached.expiresAt > now) {
|
||||
return cached.models;
|
||||
}
|
||||
|
||||
|
|
@ -99,6 +100,14 @@ export async function listCodexModels(): Promise<AdapterModel[]> {
|
|||
return fallback;
|
||||
}
|
||||
|
||||
export async function listCodexModels(): Promise<AdapterModel[]> {
|
||||
return loadCodexModels();
|
||||
}
|
||||
|
||||
export async function refreshCodexModels(): Promise<AdapterModel[]> {
|
||||
return loadCodexModels({ forceRefresh: true });
|
||||
}
|
||||
|
||||
export function resetCodexModelsCacheForTests() {
|
||||
cached = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
export {
|
||||
getServerAdapter,
|
||||
listAdapterModels,
|
||||
refreshAdapterModels,
|
||||
listServerAdapters,
|
||||
findServerAdapter,
|
||||
findActiveServerAdapter,
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ import {
|
|||
agentConfigurationDoc as openclawGatewayAgentConfigurationDoc,
|
||||
models as openclawGatewayModels,
|
||||
} from "@paperclipai/adapter-openclaw-gateway";
|
||||
import { listCodexModels } from "./codex-models.js";
|
||||
import { listCodexModels, refreshCodexModels } from "./codex-models.js";
|
||||
import { listCursorModels } from "./cursor-models.js";
|
||||
import {
|
||||
execute as piExecute,
|
||||
|
|
@ -145,6 +145,7 @@ const codexLocalAdapter: ServerAdapterModule = {
|
|||
sessionManagement: getAdapterSessionManagement("codex_local") ?? undefined,
|
||||
models: codexModels,
|
||||
listModels: listCodexModels,
|
||||
refreshModels: refreshCodexModels,
|
||||
supportsLocalAgentJwt: true,
|
||||
supportsInstructionsBundle: true,
|
||||
instructionsPathKey: "instructionsFilePath",
|
||||
|
|
@ -459,6 +460,20 @@ export async function listAdapterModels(type: string): Promise<{ id: string; lab
|
|||
return adapter.models ?? [];
|
||||
}
|
||||
|
||||
export async function refreshAdapterModels(type: string): Promise<{ id: string; label: string }[]> {
|
||||
const adapter = findActiveServerAdapter(type);
|
||||
if (!adapter) return [];
|
||||
if (adapter.refreshModels) {
|
||||
const refreshed = await adapter.refreshModels();
|
||||
if (refreshed.length > 0) return refreshed;
|
||||
}
|
||||
if (adapter.listModels) {
|
||||
const discovered = await adapter.listModels();
|
||||
if (discovered.length > 0) return discovered;
|
||||
}
|
||||
return adapter.models ?? [];
|
||||
}
|
||||
|
||||
export function listServerAdapters(): ServerAdapterModule[] {
|
||||
return Array.from(adaptersByType.values());
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue