mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-05 00:34:22 +08:00
feat(agent): enhance Codex initialization with MCP preheating and structured status updates
This commit is contained in:
@@ -258,6 +258,18 @@ export class CodexAppClient {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (method === "mcpServer/startupStatus/updated") {
|
||||
const value = params as unknown as CodexNotificationParams<"mcpServer/startupStatus/updated">;
|
||||
this.emit("agent_bootstrap", {
|
||||
type: "mcp.startup",
|
||||
threadId: value.threadId || this.currentThreadId,
|
||||
name: value.name,
|
||||
status: value.status,
|
||||
error: value.error,
|
||||
failureReason: value.failureReason,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const turnEvent = method.startsWith("turn/") || method.startsWith("item/") || method === "thread/tokenUsage/updated" || method === "error";
|
||||
if (turnEvent) {
|
||||
const threadId = String(field(params, "threadId") || this.currentThreadId);
|
||||
|
||||
@@ -6,6 +6,7 @@ export type CodexTurnError = JsonRecord & { message: string };
|
||||
export type CodexItem = JsonRecord & { id: string; type: string; text?: string };
|
||||
export type CodexPlanStep = { step: string; status: "pending" | "inProgress" | "completed" };
|
||||
export type CodexPlanUpdate = { threadId: string; turnId: string; explanation?: string | null; plan: CodexPlanStep[]; turnStatus?: string };
|
||||
export type CodexMcpStartupStatus = { threadId: string | null; name: string; status: "starting" | "ready" | "failed" | "cancelled"; error: string | null; failureReason: "reauthenticationRequired" | null };
|
||||
export type CodexReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh" | "max" | "ultra";
|
||||
export type CodexModel = JsonRecord & {
|
||||
id: string;
|
||||
@@ -99,6 +100,7 @@ type CodexNotificationSpec = {
|
||||
"item/reasoning/summaryTextDelta": { threadId: string; turnId: string; itemId: string; delta: string; summaryIndex: number };
|
||||
"item/commandExecution/outputDelta": { threadId: string; turnId: string; itemId: string; delta: string };
|
||||
"thread/tokenUsage/updated": { threadId: string; turnId: string; tokenUsage: { last: TokenUsageBreakdown } };
|
||||
"mcpServer/startupStatus/updated": CodexMcpStartupStatus;
|
||||
error: { threadId: string; turnId: string; error: CodexTurnError; willRetry: boolean };
|
||||
};
|
||||
|
||||
|
||||
@@ -43,6 +43,24 @@ export function startHttpServer() {
|
||||
session.emitThread("workspace_changed", activeThreadId, { ...payload, activeThreadId });
|
||||
return workspace;
|
||||
};
|
||||
let draftThreadStart: ReturnType<typeof startCodexThread> | null = null;
|
||||
const prepareDraftThread = (clientId: string, permission: AgentPermissionMode) => {
|
||||
if (draftThreadStart) return draftThreadStart;
|
||||
const workspace = ensureSiteWorkspace(config);
|
||||
emit("agent_bootstrap", { type: "codex.preparing", sourceClientId: clientId });
|
||||
const start = startCodexThread(emit, workspace.workspacePath, permission);
|
||||
draftThreadStart = start;
|
||||
void start.then((thread) => {
|
||||
if (draftThreadStart !== start) return;
|
||||
draftThreadStart = null;
|
||||
const threadId = String((thread as Record<string, unknown>).id || "");
|
||||
if (threadId && !ensureSiteWorkspace(config).activeThreadId) setActiveThread(threadId, { emptyThread: true, draftThread: true, sourceClientId: clientId });
|
||||
}).catch((error) => {
|
||||
if (draftThreadStart === start) draftThreadStart = null;
|
||||
emit("agent_bootstrap", { type: "codex.prepare_failed", sourceClientId: clientId, error: error instanceof Error ? error.message : String(error) });
|
||||
});
|
||||
return start;
|
||||
};
|
||||
const app = express();
|
||||
app.disable("x-powered-by");
|
||||
app.use(express.json({ limit: "30mb" }));
|
||||
@@ -124,7 +142,10 @@ export function startHttpServer() {
|
||||
res.json({ ok: true, workspace: nextWorkspace, thread: summarizeCodexThread(thread), messages: [] });
|
||||
}));
|
||||
app.post("/agent/codex/threads/reset", codexMutation((req, res) => {
|
||||
res.json({ ok: true, workspace: setActiveThread("", { emptyThread: true, draftThread: true, sourceClientId: String(req.body?.clientId || "") }) });
|
||||
const clientId = String(req.body?.clientId || "");
|
||||
const workspace = setActiveThread("", { emptyThread: true, draftThread: true, sourceClientId: clientId });
|
||||
void prepareDraftThread(clientId, permissionMode(req.body?.permissionMode));
|
||||
res.json({ ok: true, workspace });
|
||||
}));
|
||||
app.get("/agent/codex/threads/:threadId", route(async (req, res) => {
|
||||
const workspace = ensureSiteWorkspace(config);
|
||||
@@ -172,7 +193,7 @@ export function startHttpServer() {
|
||||
try {
|
||||
let turnId = "";
|
||||
if (!threadId) {
|
||||
const thread = await startCodexThread(emit, workspace.workspacePath, permissionMode(req.body?.permissionMode));
|
||||
const thread = await prepareDraftThread(clientId, permissionMode(req.body?.permissionMode));
|
||||
threadId = String((thread as Record<string, unknown>).id || "");
|
||||
setActiveThread(threadId, { emptyThread: true, sourceClientId: clientId });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user