diff --git a/package-lock.json b/package-lock.json index 12a81270..454f184f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "vscode-jsonrpc": "^8.2.1" }, "devDependencies": { - "@openai/codex": "^0.114.0", + "@openai/codex": "^0.116.0", "@types/node": "^24.10.1", "mcp-hello-world": "^1.1.2", "tsx": "^4.20.6", @@ -813,9 +813,9 @@ } }, "node_modules/@openai/codex": { - "version": "0.114.0", - "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0.tgz", - "integrity": "sha512-HMo8LRR6CtfKkaa28xvFK6eOarmBFTDfsrS9GJtEoaspGGemFok494CpafDspiTZaHZZGHfSUe5SWTUxYq7OaA==", + "version": "0.116.0", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.116.0.tgz", + "integrity": "sha512-K6q9P2ZmpnzGmpS6Ybjvsdtvu8AbJx3f/Z4KmjH1u85StSS9TWMSQB8z0PPObKMejbtiIkHwhGyEIHi4iBYjig==", "dev": true, "license": "Apache-2.0", "bin": { @@ -825,19 +825,19 @@ "node": ">=16" }, "optionalDependencies": { - "@openai/codex-darwin-arm64": "npm:@openai/codex@0.114.0-darwin-arm64", - "@openai/codex-darwin-x64": "npm:@openai/codex@0.114.0-darwin-x64", - "@openai/codex-linux-arm64": "npm:@openai/codex@0.114.0-linux-arm64", - "@openai/codex-linux-x64": "npm:@openai/codex@0.114.0-linux-x64", - "@openai/codex-win32-arm64": "npm:@openai/codex@0.114.0-win32-arm64", - "@openai/codex-win32-x64": "npm:@openai/codex@0.114.0-win32-x64" + "@openai/codex-darwin-arm64": "npm:@openai/codex@0.116.0-darwin-arm64", + "@openai/codex-darwin-x64": "npm:@openai/codex@0.116.0-darwin-x64", + "@openai/codex-linux-arm64": "npm:@openai/codex@0.116.0-linux-arm64", + "@openai/codex-linux-x64": "npm:@openai/codex@0.116.0-linux-x64", + "@openai/codex-win32-arm64": "npm:@openai/codex@0.116.0-win32-arm64", + "@openai/codex-win32-x64": "npm:@openai/codex@0.116.0-win32-x64" } }, "node_modules/@openai/codex-darwin-arm64": { "name": "@openai/codex", - "version": "0.114.0-darwin-arm64", - "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-darwin-arm64.tgz", - "integrity": "sha512-c9dlgo9O+66alr3s3G36fKE3KaO2+xrLZ/QcU3oujDruV86pqgVSEK2njHi+WIyyOa6m2AyWxdaHLEbKxPCNRg==", + "version": "0.116.0-darwin-arm64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.116.0-darwin-arm64.tgz", + "integrity": "sha512-WkdL083p8uMeASpg8bwV0DPGgzkm48LjN3MyU2m/YukujbiLnknAmG29O2q2rFCLm0oLSDIGUK8EnXA4ZcAF9Q==", "cpu": [ "arm64" ], @@ -853,9 +853,9 @@ }, "node_modules/@openai/codex-darwin-x64": { "name": "@openai/codex", - "version": "0.114.0-darwin-x64", - "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-darwin-x64.tgz", - "integrity": "sha512-oPAPeZSaJZ1AQneFPSJu44uqbZr63Bxut9FOTWZwuSqYx4YEees7i0HJmJcqQc0XnCbYtHJdqo/i07Uv374NLw==", + "version": "0.116.0-darwin-x64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.116.0-darwin-x64.tgz", + "integrity": "sha512-Ax8uTwYSNIwGrzcNRcn0jJQhZzNcKGDbbn00Emde7gGOemjSLhRALjUaKjckAaW5xWnNqHTGdtzzPB4phNlDYg==", "cpu": [ "x64" ], @@ -871,9 +871,9 @@ }, "node_modules/@openai/codex-linux-arm64": { "name": "@openai/codex", - "version": "0.114.0-linux-arm64", - "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-linux-arm64.tgz", - "integrity": "sha512-oUFZGZgMiEqmo85C+dfhckpVApH25alLWfwBgfKr2IRgdx/tovy8vN101f6kj01E6nDDe4LfJdNSZGtZht4fRA==", + "version": "0.116.0-linux-arm64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.116.0-linux-arm64.tgz", + "integrity": "sha512-X7cL8rBSGDB+RSZc2FoKiqcMVeLPMmo06bkss/en4lLQsV1XG2DZI56WuXg92IOX3SjYl6Av/eOWgsb1t3UeLQ==", "cpu": [ "arm64" ], @@ -889,9 +889,9 @@ }, "node_modules/@openai/codex-linux-x64": { "name": "@openai/codex", - "version": "0.114.0-linux-x64", - "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-linux-x64.tgz", - "integrity": "sha512-MF6RhxfBoccccd5CYII2C7TL2TvYzombBQhanDZfQAajr6n7IuoazCmoHDlrFHS4AcSw9bYA4MRlrwEjbEjgsw==", + "version": "0.116.0-linux-x64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.116.0-linux-x64.tgz", + "integrity": "sha512-S9InOgJT3tj6uQp55NqrCA1k5tklwFaH00JdC2ElbRmxchm7ard4WxHSJZX9TiY8enj4cQoLIC04NFTUCO+/PQ==", "cpu": [ "x64" ], @@ -907,9 +907,9 @@ }, "node_modules/@openai/codex-win32-arm64": { "name": "@openai/codex", - "version": "0.114.0-win32-arm64", - "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-win32-arm64.tgz", - "integrity": "sha512-CnRMHopj3en9aqQ2UaDW7EgpEGkxHdZVLLRq2cOy5D0HyuzF6Qb6595ADoFVJHEmPeN5Iz/KUbiGs5GLDjUwOA==", + "version": "0.116.0-win32-arm64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.116.0-win32-arm64.tgz", + "integrity": "sha512-kX2oAUzkgZX9OsYpd4omv9IGf+9VWj4Vy3UtIAnQKBu1DTSzmTJmXDuDn87mkyUciSZadm2QbeqQQzm2NC0NYw==", "cpu": [ "arm64" ], @@ -925,9 +925,9 @@ }, "node_modules/@openai/codex-win32-x64": { "name": "@openai/codex", - "version": "0.114.0-win32-x64", - "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.114.0-win32-x64.tgz", - "integrity": "sha512-ztRsH5Z+gPVZ5ZInx6HzXsTFFkuEdjk3Ay0UGVOhRRCWvevXQi/SL4eU8hwysCYrs8K/WRq3mo4c95w9zQ2wLw==", + "version": "0.116.0-win32-x64", + "resolved": "https://registry.npmjs.org/@openai/codex/-/codex-0.116.0-win32-x64.tgz", + "integrity": "sha512-6sBIMOoA9FNuxQvCCnK0P548Wqrlk3I9SMdtOCUg2zYzYU7jOF2mWS1VpRQ6R+Jvo2x50dxeJZ+W37dBmXfprw==", "cpu": [ "x64" ], diff --git a/package.json b/package.json index 765a42cb..3fa20c59 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "license": "ISC", "type": "module", "devDependencies": { - "@openai/codex": "^0.114.0", + "@openai/codex": "^0.116.0", "@types/node": "^24.10.1", "mcp-hello-world": "^1.1.2", "tsx": "^4.20.6", diff --git a/src/CodexAcpClient.ts b/src/CodexAcpClient.ts index 8451ffd0..55e84d67 100644 --- a/src/CodexAcpClient.ts +++ b/src/CodexAcpClient.ts @@ -34,7 +34,6 @@ import type {AuthenticationLogoutResponse, AuthenticationStatusResponse} from ". * Converts ACP requests into corresponding app-server operations. */ export class CodexAcpClient { - private readonly codexClient: CodexAppServerClient; private readonly config: JsonObject; private readonly modelProvider: string | null; @@ -69,19 +68,27 @@ export class CodexAcpClient { } switch (authRequest.methodId) { - case "api-key": + case "api-key": { if (!authRequest._meta || !authRequest._meta["api-key"]) throw RequestError.invalidRequest(); + const loginCompletedVersion = this.codexClient.getAccountLoginCompletedVersion(); await this.codexClient.accountLogin({ type: "apiKey", apiKey: authRequest._meta["api-key"].apiKey }); - break; - case "chat-gpt": + this.gatewayConfig = null; + const result = await this.codexClient.awaitLoginCompleted(loginCompletedVersion); + return result.success; + } + case "chat-gpt": { + const loginCompletedVersion = this.codexClient.getAccountLoginCompletedVersion(); const loginResponse = await this.codexClient.accountLogin({type: "chatgpt"}); if (loginResponse.type == "chatgpt") { await open(loginResponse.authUrl); } - break; + this.gatewayConfig = null; + const result = await this.codexClient.awaitLoginCompleted(loginCompletedVersion); + return result.success; + } case "gateway": if (!authRequest._meta) throw RequestError.invalidRequest(); @@ -111,9 +118,7 @@ export class CodexAcpClient { // Reset the gateway config to null if another authentication method was used this.gatewayConfig = null; - - const result = await this.codexClient.awaitLoginCompleted() - return result.success; + return false; } @@ -154,8 +159,9 @@ export class CodexAcpClient { } async logout(): Promise { + const accountUpdatedVersion = this.codexClient.getAccountUpdatedVersion(); await this.codexClient.accountLogout(); - await this.codexClient.awaitAccountUpdated(); + await this.codexClient.awaitAccountUpdated(accountUpdatedVersion); return {}; } @@ -178,6 +184,7 @@ export class CodexAcpClient { async resumeSession(request: acp.ResumeSessionRequest): Promise { await this.refreshSkills(request.cwd, request._meta); + const mcpStartupVersion = this.codexClient.getMcpStartupCompleteVersion(); const response = await this.codexClient.threadResume({ approvalPolicy: null, @@ -200,10 +207,12 @@ export class CodexAcpClient { sessionId: request.sessionId, currentModelId: currentModelId, models: codexModels, + mcpStartupVersion, } } async loadSession(request: acp.LoadSessionRequest): Promise { + const mcpStartupVersion = this.codexClient.getMcpStartupCompleteVersion(); const response = await this.codexClient.threadResume({ approvalPolicy: null, sandbox: null, @@ -226,11 +235,13 @@ export class CodexAcpClient { currentModelId: currentModelId, models: codexModels, thread: response.thread, + mcpStartupVersion, }; } async newSession(request: acp.NewSessionRequest): Promise { await this.refreshSkills(request.cwd, request._meta); + const mcpStartupVersion = this.codexClient.getMcpStartupCompleteVersion(); const response = await this.codexClient.threadStart({ config: this.createSessionConfig(request.cwd, request.mcpServers), @@ -256,12 +267,13 @@ export class CodexAcpClient { sessionId: response.thread.id, currentModelId: currentModelId, models: codexModels, + mcpStartupVersion, }; } - async awaitMcpServers(): Promise>{ - const response = await this.codexClient.awaitMcpStartup(); - return response.ready; + async awaitMcpStartup(mcpStartupVersion: number): Promise> { + const startup = await this.codexClient.awaitMcpStartup(mcpStartupVersion); + return startup.ready; } private createSessionConfig(projectPath: string, mcpServers: Array): JsonObject { @@ -390,6 +402,7 @@ export class CodexAcpClient { "vscode", "exec", "appServer", + "custom", "subAgent", "subAgentReview", "subAgentCompact", @@ -523,6 +536,7 @@ export type SessionMetadata = { sessionId: string, currentModelId: string, models: Model[], + mcpStartupVersion: number, } export type SessionMetadataWithThread = SessionMetadata & { diff --git a/src/CodexAcpServer.ts b/src/CodexAcpServer.ts index 24e916fd..3864c260 100644 --- a/src/CodexAcpServer.ts +++ b/src/CodexAcpServer.ts @@ -10,7 +10,7 @@ import {CodexApprovalHandler} from "./CodexApprovalHandler"; import {CodexAuthMethods, type CodexAuthRequest} from "./CodexAuthMethod"; import {CodexAcpClient, type SessionMetadata, type SessionMetadataWithThread} from "./CodexAcpClient"; import {ACPSessionConnection, type UpdateSessionEvent} from "./ACPSessionConnection"; -import type {Account, CollabAgentToolCallStatus, Model, RateLimitSnapshot, Thread, ThreadItem, UserInput, ReasoningEffortOption} from "./app-server/v2"; +import type {Account, CollabAgentToolCallStatus, Model, Thread, ThreadItem, UserInput, ReasoningEffortOption} from "./app-server/v2"; import type {RateLimitsMap} from "./RateLimitsMap"; import type {InputModality, ReasoningEffort} from "./app-server"; import {ModelId} from "./ModelId"; @@ -127,7 +127,6 @@ export class CodexAcpServer implements acp.Agent { async getOrCreateSession(request: acp.NewSessionRequest | acp.ResumeSessionRequest): Promise<[SessionId, SessionModelState, SessionModeState]> { await this.checkAuthorization(); - const pendingMcpServers: Promise> = this.codexAcpClient.awaitMcpServers(); let sessionMetadata: SessionMetadata; if ("sessionId" in request) { @@ -139,9 +138,8 @@ export class CodexAcpServer implements acp.Agent { } const accountResponse = await this.runWithProcessCheck(() => this.codexAcpClient.getAccount()); - const {sessionId, currentModelId, models} = sessionMetadata; - logger.log(`Waiting MCP servers to start...`) - const sessionMcpServers = await pendingMcpServers; + const {sessionId, currentModelId, models, mcpStartupVersion} = sessionMetadata; + const sessionMcpServers = await this.resolveSessionMcpServers(request.mcpServers ?? [], mcpStartupVersion, "sessionId" in request); const currentModel = this.findCurrentModel(models, currentModelId); const sessionState: SessionState = { sessionId: sessionId, @@ -332,7 +330,6 @@ export class CodexAcpServer implements acp.Agent { thread: Thread; }> { await this.checkAuthorization(); - const pendingMcpServers: Promise> = this.codexAcpClient.awaitMcpServers(); logger.log(`Load existing session: ${request.sessionId}...`); const sessionMetadata: SessionMetadataWithThread = await this.runWithProcessCheck(() => @@ -340,9 +337,8 @@ export class CodexAcpServer implements acp.Agent { ); const accountResponse = await this.runWithProcessCheck(() => this.codexAcpClient.getAccount()); - const {sessionId, currentModelId, models, thread} = sessionMetadata; - logger.log("Waiting MCP servers to start..."); - const sessionMcpServers = await pendingMcpServers; + const {sessionId, currentModelId, models, thread, mcpStartupVersion} = sessionMetadata; + const sessionMcpServers = await this.resolveSessionMcpServers(request.mcpServers ?? [], mcpStartupVersion, true); const currentModel = this.findCurrentModel(models, currentModelId); const sessionState: SessionState = { sessionId: sessionId, @@ -601,6 +597,22 @@ export class CodexAcpServer implements acp.Agent { return sessionState; } + private async resolveSessionMcpServers( + mcpServers: Array, + mcpStartupVersion: number, + recoverFromStartup: boolean, + ): Promise> { + const requestedServerNames = getRequestedMcpServerNames(mcpServers); + if (requestedServerNames.length > 0) { + return requestedServerNames; + } + if (!recoverFromStartup) { + return []; + } + logger.log("Recovering MCP servers from startup state..."); + return await this.runWithProcessCheck(() => this.codexAcpClient.awaitMcpStartup(mcpStartupVersion)); + } + async prompt(params: acp.PromptRequest): Promise { logger.log("Prompt received", { sessionId: params.sessionId, @@ -747,3 +759,7 @@ export class CodexAcpServer implements acp.Agent { } } } + +function getRequestedMcpServerNames(mcpServers: Array): Array { + return Array.from(new Set(mcpServers.map(server => server.name))); +} diff --git a/src/CodexAppServerClient.ts b/src/CodexAppServerClient.ts index c091c2e0..f9fb6273 100644 --- a/src/CodexAppServerClient.ts +++ b/src/CodexAppServerClient.ts @@ -1,8 +1,9 @@ import {type MessageConnection, RequestType} from "vscode-jsonrpc/node"; import type { - ClientRequest, ConversationId, + ClientRequest, InitializeParams, - InitializeResponse, McpStartupCompleteEvent, + InitializeResponse, + McpStartupCompleteEvent, ServerNotification } from "./app-server"; import type { @@ -52,11 +53,6 @@ const FileChangeApprovalRequest = new RequestType< void >('item/fileChange/requestApproval'); -/** - * Poorly supported/deprecated event types - */ -export type McpStartupCompleteNotification = { method: "codex/event/mcp_startup_complete", params: { id?: string, msg: McpStartupCompleteEvent & { type:"mcp_startup_complete" }, conversationId?: ConversationId } } - /** * A type-safe client over the Codex App Server's JSON-RPC API. * Maps each request to its expected response and exposes clear, typed methods for supported JSON-RPC operations. @@ -64,16 +60,43 @@ export type McpStartupCompleteNotification = { method: "codex/event/mcp_startup_ export class CodexAppServerClient { readonly connection: MessageConnection; private approvalHandlers = new Map(); + private accountLoginCompletedVersion = 0; + private lastAccountLoginCompleted: AccountLoginCompletedNotification | null = null; + private readonly accountLoginCompletedResolvers: Array> = []; + private accountUpdatedVersion = 0; + private lastAccountUpdated: AccountUpdatedNotification | null = null; + private readonly accountUpdatedResolvers: Array> = []; + private mcpStartupCompleteVersion = 0; + private lastMcpStartupComplete: McpStartupCompleteEvent | null = null; + private readonly mcpStartupCompleteResolvers: Array> = []; constructor(connection: MessageConnection) { this.connection = connection; + this.connection.onNotification("account/login/completed", (event: AccountLoginCompletedNotification) => { + this.accountLoginCompletedVersion += 1; + this.lastAccountLoginCompleted = event; + this.resolveSignal(event, this.accountLoginCompletedVersion, this.accountLoginCompletedResolvers); + }); + this.connection.onNotification("account/updated", (event: AccountUpdatedNotification) => { + this.accountUpdatedVersion += 1; + this.lastAccountUpdated = event; + this.resolveSignal(event, this.accountUpdatedVersion, this.accountUpdatedResolvers); + }); this.connection.onUnhandledNotification((data) => { - const serverNotification = data as ServerNotification ?? null; - if (serverNotification) { - this.notify(serverNotification); + if (isMcpStartupCompleteNotification(data)) { + this.mcpStartupCompleteVersion += 1; + this.lastMcpStartupComplete = data.params.msg; + this.resolveSignal(data.params.msg, this.mcpStartupCompleteVersion, this.mcpStartupCompleteResolvers); + for (const callback of this.codexEventHandlers) { + callback({ eventType: "notification", ...data }); + } + return; } + + const serverNotification = data as ServerNotification; + this.notify(serverNotification); for (const callback of this.codexEventHandlers) { - callback({ eventType: "notification", ...serverNotification}); + callback({ eventType: "notification", ...serverNotification }); } }); @@ -146,28 +169,43 @@ export class CodexAppServerClient { return await this.sendRequest({ method: "config/read", params: params }); } - async awaitLoginCompleted(): Promise { - return await new Promise((resolve) => { - this.connection.onNotification("account/login/completed", (event: AccountLoginCompletedNotification) => { - resolve(event); - }); - }); + getAccountLoginCompletedVersion(): number { + return this.accountLoginCompletedVersion; } - async awaitAccountUpdated(): Promise { - return await new Promise((resolve) => { - this.connection.onNotification("account/updated", (event: AccountUpdatedNotification) => { - resolve(event); - }); - }); + async awaitLoginCompleted(afterVersion: number): Promise { + return await this.awaitSignal( + this.lastAccountLoginCompleted, + this.accountLoginCompletedVersion, + afterVersion, + this.accountLoginCompletedResolvers + ); } - async awaitMcpStartup(): Promise { - return await new Promise((resolve) => { - this.connection.onNotification("codex/event/mcp_startup_complete", (event: McpStartupCompleteNotification["params"]) => { - resolve(event.msg); - }); - }); + getAccountUpdatedVersion(): number { + return this.accountUpdatedVersion; + } + + async awaitAccountUpdated(afterVersion: number): Promise { + return await this.awaitSignal( + this.lastAccountUpdated, + this.accountUpdatedVersion, + afterVersion, + this.accountUpdatedResolvers + ); + } + + getMcpStartupCompleteVersion(): number { + return this.mcpStartupCompleteVersion; + } + + async awaitMcpStartup(afterVersion: number): Promise { + return await this.awaitSignal( + this.lastMcpStartupComplete, + this.mcpStartupCompleteVersion, + afterVersion, + this.mcpStartupCompleteResolvers + ); } async accountRead(params: GetAccountParams): Promise { @@ -211,6 +249,36 @@ export class CodexAppServerClient { } } + private resolveSignal( + event: T, + version: number, + resolvers: Array> + ): void { + const pendingResolvers: Array> = []; + for (const resolver of resolvers) { + if (resolver.afterVersion < version) { + resolver.resolve(event); + } else { + pendingResolvers.push(resolver); + } + } + resolvers.splice(0, resolvers.length, ...pendingResolvers); + } + + private async awaitSignal( + lastEvent: T | null, + currentVersion: number, + afterVersion: number, + resolvers: Array> + ): Promise { + if (lastEvent !== null && currentVersion > afterVersion) { + return lastEvent; + } + return await new Promise((resolve) => { + resolvers.push({afterVersion, resolve}); + }); + } + private async sendRequest(request: CodexRequest): Promise { for (const callback of this.codexEventHandlers) { callback({ eventType: "request", ...request}); @@ -229,10 +297,32 @@ export class CodexAppServerClient { } } -export type CodexConnectionEvent = { eventType: "request" } & CodexRequest | { eventType: "response" } & unknown | { eventType: "notification" } & ServerNotification; +export type CodexConnectionEvent = + | ({ eventType: "request" } & CodexRequest) + | ({ eventType: "response" } & unknown) + | ({ eventType: "notification" } & (ServerNotification | McpStartupCompleteNotification)); type CodexRequest = DistributiveOmit type DistributiveOmit = T extends any ? Omit : never; + +type SignalResolver = { + afterVersion: number; + resolve: (event: T) => void; +}; + +type McpStartupCompleteNotification = { + method: "codex/event/mcp_startup_complete", + params: { + msg: McpStartupCompleteEvent & { type: "mcp_startup_complete" } + } +}; + +function isMcpStartupCompleteNotification(value: unknown): value is McpStartupCompleteNotification { + return typeof value === "object" + && value !== null + && "method" in value + && value.method === "codex/event/mcp_startup_complete"; +} diff --git a/src/CodexEventHandler.ts b/src/CodexEventHandler.ts index 89e88de7..5eb62c3e 100644 --- a/src/CodexEventHandler.ts +++ b/src/CodexEventHandler.ts @@ -71,6 +71,9 @@ export class CodexEventHandler { return await this.createTextEvent(notification.params); case "item/started": return await this.createItemEvent(notification.params); + case "item/autoApprovalReview/started": + case "item/autoApprovalReview/completed": + return null; case "item/completed": return await this.completeItemEvent(notification.params); case "turn/plan/updated": diff --git a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts index a8179e7b..bb1ecdc3 100644 --- a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts +++ b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts @@ -42,7 +42,7 @@ describe('ACP server test', { timeout: 40_000 }, () => { vi.clearAllMocks(); }); - const ignoredFields = ["thread", "cwd", "id", "createdAt", "path", "threadId", "userAgent", "sandbox", "conversationId", "origins", "supportedReasoningEfforts", "reasoningEffort", "model", "readOnlyAccess"]; + const ignoredFields = ["thread", "cwd", "id", "createdAt", "path", "threadId", "userAgent", "sandbox", "conversationId", "origins", "supportedReasoningEfforts", "reasoningEffort", "model", "readOnlyAccess", "approvalsReviewer"]; it.skip('should start conversation', async () => { const codexAcpAgent = fixture.getCodexAcpAgent(); @@ -95,9 +95,50 @@ describe('ACP server test', { timeout: 40_000 }, () => { const newSessionResponse = await codexAcpAgent.newSession({cwd: "", mcpServers: []}); expect(newSessionResponse.sessionId).toBeDefined() - const transportDump = keyFixture.getCodexConnectionDump([...ignoredFields, "upgrade"]); - await expect(transportDump).toMatchFileSnapshot("data/auth-with-key.json"); - + const transportEvents = keyFixture.getCodexConnectionEvents([...ignoredFields, "upgrade"]); + const transportMethods = transportEvents.flatMap(event => "method" in event ? [event.method] : []); + const loginRequest = transportEvents.find(event => + event.eventType === "request" && + "method" in event && + event.method === "account/login/start" + ); + const loginResponse = transportEvents.find(event => + event.eventType === "response" && + "type" in event && + event.type === "apiKey" + ); + const threadStartResponse = transportEvents.find(event => + event.eventType === "response" && + "modelProvider" in event && + "approvalPolicy" in event + ); + expect(transportMethods).toEqual([ + "account/login/start", + "account/read", + "thread/start", + "model/list", + "thread/started", + "account/read", + "skills/list", + ]); + expect(loginRequest).toEqual({ + eventType: "request", + method: "account/login/start", + params: { + type: "apiKey", + apiKey: "TOKEN", + } + }); + expect(loginResponse).toEqual({ + eventType: "response", + type: "apiKey", + }); + expect(threadStartResponse).toMatchObject({ + eventType: "response", + modelProvider: "openai", + approvalPolicy: "on-request", + approvalsReviewer: "approvalsReviewer", + }); const authenticatedResponse = await keyFixture.getCodexAcpAgent().extMethod("authentication/status", {}); expect(authenticatedResponse).toEqual({type: "api-key"}); @@ -108,31 +149,34 @@ describe('ACP server test', { timeout: 40_000 }, () => { }); it('should authenticate with a gateway', async () => { - const codexAcpAgent = fixture.getCodexAcpAgent(); - - await codexAcpAgent.initialize({protocolVersion: 1}); - await fixture.getCodexAcpClient().logout(); + await overrideCodexHome('cli_auth_credentials_store = "file"', async () => { + const gatewayFixture = createTestFixture(); + const codexAcpAgent = gatewayFixture.getCodexAcpAgent(); - const authRequest: CodexAuthRequest = { - methodId: "gateway", - _meta: { - "gateway": { - baseUrl: "https://www.example.com", - headers: { - "Custom-Auth-Header": "TOKEN" + await codexAcpAgent.initialize({protocolVersion: 1}); + await gatewayFixture.getCodexAcpClient().logout(); + + const authRequest: CodexAuthRequest = { + methodId: "gateway", + _meta: { + "gateway": { + baseUrl: "https://www.example.com", + headers: { + "Custom-Auth-Header": "TOKEN" + } } } - } - }; + }; - await codexAcpAgent.authenticate(authRequest); - expect(await fixture.getCodexAcpClient().authRequired()).toBe(false); + await codexAcpAgent.authenticate(authRequest); + expect(await gatewayFixture.getCodexAcpClient().authRequired()).toBe(false); - const authenticatedResponse = await fixture.getCodexAcpAgent().extMethod("authentication/status", {}); - expect(authenticatedResponse).toEqual({type: "gateway", name: "custom-gateway"}); + const authenticatedResponse = await gatewayFixture.getCodexAcpAgent().extMethod("authentication/status", {}); + expect(authenticatedResponse).toEqual({type: "gateway", name: "custom-gateway"}); - const newSessionResponse = await codexAcpAgent.newSession({cwd: "", mcpServers: []}); - expect(newSessionResponse.sessionId).toBeDefined() + const newSessionResponse = await codexAcpAgent.newSession({cwd: "", mcpServers: []}); + expect(newSessionResponse.sessionId).toBeDefined() + }); }) it('prefetches session additional skill roots before thread start', async () => { @@ -188,6 +232,30 @@ describe('ACP server test', { timeout: 40_000 }, () => { expect(listSkillsSpy.mock.invocationCallOrder[0]!).toBeLessThan(threadStartSpy.mock.invocationCallOrder[0]!); }); + it('waits for mcp startup completion and returns ready servers', async () => { + const mockFixture = createCodexMockTestFixture(); + const codexAcpClient = mockFixture.getCodexAcpClient(); + const codexAppServerClient = mockFixture.getCodexAppServerClient(); + + const startupPromise = codexAcpClient.awaitMcpStartup(codexAppServerClient.getMcpStartupCompleteVersion()); + + mockFixture.sendServerNotification({ + method: "codex/event/mcp_startup_complete", + params: { + msg: { + type: "mcp_startup_complete", + ready: ["alpha", "beta"], + failed: [], + cancelled: [], + } + } + }); + + const mcpServers = await startupPromise; + + expect(mcpServers).toEqual(["alpha", "beta"]); + }); + it('prefetches session additional skill roots before turn start', async () => { const mockFixture = createCodexMockTestFixture(); const codexAcpAgent = mockFixture.getCodexAcpAgent(); diff --git a/src/__tests__/CodexACPAgent/data/auth-with-key.json b/src/__tests__/CodexACPAgent/data/auth-with-key.json index b9a34c96..751ccde8 100644 --- a/src/__tests__/CodexACPAgent/data/auth-with-key.json +++ b/src/__tests__/CodexACPAgent/data/auth-with-key.json @@ -232,4 +232,4 @@ "eventType": "request", "method": "skills/list", "params": {} -} \ No newline at end of file +} diff --git a/src/__tests__/CodexACPAgent/list-sessions.test.ts b/src/__tests__/CodexACPAgent/list-sessions.test.ts index 32390314..cdf0801b 100644 --- a/src/__tests__/CodexACPAgent/list-sessions.test.ts +++ b/src/__tests__/CodexACPAgent/list-sessions.test.ts @@ -65,6 +65,21 @@ describe("CodexACPAgent - list sessions", () => { }; const response = await codexAcpAgent.unstable_listSessions(params); + expect(codexAppServerClient.threadList).toHaveBeenCalledWith(expect.objectContaining({ + sourceKinds: [ + "cli", + "vscode", + "exec", + "appServer", + "custom", + "subAgent", + "subAgentReview", + "subAgentCompact", + "subAgentThreadSpawn", + "subAgentOther", + "unknown", + ], + })); await expect(JSON.stringify(response, null, 2)).toMatchFileSnapshot( "data/list-sessions.json" ); diff --git a/src/__tests__/CodexACPAgent/load-session.test.ts b/src/__tests__/CodexACPAgent/load-session.test.ts index 9be09697..5aa55d2c 100644 --- a/src/__tests__/CodexACPAgent/load-session.test.ts +++ b/src/__tests__/CodexACPAgent/load-session.test.ts @@ -15,7 +15,7 @@ describe("CodexACPAgent - loadSession", () => { account: null, requiresOpenaiAuth: false, }); - codexAcpClient.awaitMcpServers = vi.fn().mockResolvedValue([]); + codexAcpClient.awaitMcpStartup = vi.fn().mockResolvedValue([]); codexAcpClient.listSkills = vi.fn().mockResolvedValue({ data: [] }); const model: Model = { @@ -76,6 +76,7 @@ describe("CodexACPAgent - loadSession", () => { id: "item-agent-1", text: "Hello!", phase: null, + memoryCitation: null, }, { type: "reasoning", @@ -161,4 +162,77 @@ describe("CodexACPAgent - loadSession", () => { "data/load-session-history.json" ); }); + + it("should recover session mcp servers during loadSession when request omits them", async () => { + const fixture = createCodexMockTestFixture(); + const codexAcpAgent = fixture.getCodexAcpAgent(); + const codexAcpClient = fixture.getCodexAcpClient(); + const codexAppServerClient = fixture.getCodexAppServerClient(); + + codexAcpClient.authRequired = vi.fn().mockResolvedValue(false); + codexAcpClient.getAccount = vi.fn().mockResolvedValue({ + account: null, + requiresOpenaiAuth: false, + }); + codexAcpClient.listSkills = vi.fn().mockResolvedValue({ data: [] }); + + const awaitMcpStartupSpy = vi.spyOn(codexAcpClient, "awaitMcpStartup").mockResolvedValue(["persisted-mcp"]); + + const model: Model = { + id: "gpt-5.2", + model: "gpt-5.2", + upgrade: null, + upgradeInfo: null, + availabilityNux: null, + displayName: "GPT-5.2", + description: "Test model", + hidden: false, + supportedReasoningEfforts: [{ reasoningEffort: "medium", description: "Medium" }], + defaultReasoningEffort: "medium", + inputModalities: ["text"], + supportsPersonality: false, + isDefault: true, + }; + + codexAppServerClient.listModels = vi.fn().mockResolvedValue({ + data: [model], + nextCursor: null, + }); + codexAppServerClient.threadResume = vi.fn().mockResolvedValue({ + thread: { + id: "session-1", + preview: "", + ephemeral: false, + modelProvider: "openai", + createdAt: 0, + updatedAt: 0, + status: { type: "idle" }, + path: null, + cwd: "/test/project", + cliVersion: "0.0.0", + source: "cli", + agentNickname: null, + agentRole: null, + gitInfo: null, + name: null, + turns: [], + }, + model: model.id, + modelProvider: "openai", + cwd: "/test/project", + approvalPolicy: "never", + sandbox: { type: "dangerFullAccess" }, + reasoningEffort: model.defaultReasoningEffort, + }); + + await codexAcpAgent.initialize({ protocolVersion: 1 }); + await codexAcpAgent.loadSession({ + sessionId: "session-1", + cwd: "/test/project", + mcpServers: [], + }); + + expect(awaitMcpStartupSpy).toHaveBeenCalledWith(0); + expect(codexAcpAgent.getSessionState("session-1").sessionMcpServers).toEqual(["persisted-mcp"]); + }); }); diff --git a/src/__tests__/CodexACPAgent/mcp-session.test.ts b/src/__tests__/CodexACPAgent/mcp-session.test.ts index fa5d223a..9857768f 100644 --- a/src/__tests__/CodexACPAgent/mcp-session.test.ts +++ b/src/__tests__/CodexACPAgent/mcp-session.test.ts @@ -26,7 +26,6 @@ describe('MCP session configuration', { timeout: 40_000 }, () => { fixture.clearAcpConnectionDump(); await codexAcpAgent.prompt({sessionId: newSessionResponse.sessionId, prompt: [{type: "text", text: "/mcp"}]}); const transportDump = fixture.getAcpConnectionDump([]); - console.log(transportDump) expect(transportDump).contain("Configured MCP servers:\\n- test-mcp"); }); diff --git a/src/__tests__/CodexACPAgent/model-filtering.test.ts b/src/__tests__/CodexACPAgent/model-filtering.test.ts index 9cdc00ba..c4d98edc 100644 --- a/src/__tests__/CodexACPAgent/model-filtering.test.ts +++ b/src/__tests__/CodexACPAgent/model-filtering.test.ts @@ -80,9 +80,9 @@ describe("Model filtering", () => { sessionId: "session-id", currentModelId: "gpt-5.2[medium]", models, + mcpStartupVersion: 0, }); vi.spyOn(codexAcpClient, "getAccount").mockResolvedValue({account: null, requiresOpenaiAuth: false}); - vi.spyOn(codexAcpClient, "awaitMcpServers").mockResolvedValue([]); const newSessionResponse = await codexAcpAgent.newSession({ cwd: "", mcpServers: [] }); const sessionModels = newSessionResponse.models; diff --git a/src/__tests__/acp-test-utils.ts b/src/__tests__/acp-test-utils.ts index c28dec2b..a25138b0 100644 --- a/src/__tests__/acp-test-utils.ts +++ b/src/__tests__/acp-test-utils.ts @@ -40,6 +40,7 @@ export interface TestFixture { getCodexAcpAgent(): CodexAcpServer, onCodexConnectionEvent(handler: (event: CodexConnectionEvent) => void): void, + getCodexConnectionEvents(ignoredFields: string[], options?: CodexConnectionDumpOptions): CodexConnectionEvent[], getCodexConnectionDump(ignoredFields: string[], options?: CodexConnectionDumpOptions): string, clearCodexConnectionDump(): void, @@ -90,11 +91,11 @@ export function createBaseTestFixture(config: ConnectionConfig): TestFixture { getCodexAcpClient(): CodexAcpClient { return codexAcpClient; }, - getCodexConnectionDump(ignoredFields: string[], options?: CodexConnectionDumpOptions): string { + getCodexConnectionEvents(ignoredFields: string[], options?: CodexConnectionDumpOptions): CodexConnectionEvent[] { const placeholderResponseMethods = new Set(options?.placeholderResponseMethods ?? []); const pendingRequestMethods: string[] = []; - const filteredEvents = transportEvents.flatMap((event) => { + return transportEvents.flatMap((event) => { switch (event.eventType) { case "request": pendingRequestMethods.push(event.method); @@ -105,14 +106,17 @@ export function createBaseTestFixture(config: ConnectionConfig): TestFixture { return [{ eventType: "response" as const, placeholder: requestMethod, - }]; + } as CodexConnectionEvent]; } break; } - return [event]; + return [anonymizeValue(event, [], new Set(ignoredFields)) as CodexConnectionEvent]; }); - return createArrayDump(filteredEvents, ignoredFields); + }, + getCodexConnectionDump(ignoredFields: string[], options?: CodexConnectionDumpOptions): string { + const filteredEvents = this.getCodexConnectionEvents(ignoredFields, options); + return createArrayDump(filteredEvents, []); }, onCodexConnectionEvent(handler: (event: CodexConnectionEvent) => void): void { codexEventHandlers.push(handler); @@ -154,7 +158,7 @@ export function createTestFixture(): TestFixture { } export interface CodexMockTestFixture extends TestFixture { - sendServerNotification(notification: ServerNotification): void, + sendServerNotification(notification: ServerNotification | Record): void, sendServerRequest(method: string, params: unknown): Promise, setPermissionResponse(response: RequestPermissionResponse): void, } @@ -210,7 +214,7 @@ export function createCodexMockTestFixture(): CodexMockTestFixture { return { ...baseFixture, - sendServerNotification(notification: ServerNotification): void { + sendServerNotification(notification: ServerNotification | Record): void { if (unhandledNotificationHandler) { unhandledNotificationHandler(notification); } @@ -229,34 +233,32 @@ export function createCodexMockTestFixture(): CodexMockTestFixture { } export function createObjectDump(obj: any, anonymizedFields: string[] = []) { - const fieldsToAnonymize = new Set(anonymizedFields); - - function anonymizeValue(value: any, path: string[]): any { - if (value === null || typeof value !== "object") { - return value; - } + return JSON.stringify(anonymizeValue(obj, [], new Set(anonymizedFields)), null, 2); +} - if (Array.isArray(value)) { - return value.map((item, index) => anonymizeValue(item, [...path, String(index)])); - } +export function createArrayDump(objects: any[], anonymizedFields: string[]): string { + return objects.map(event => createObjectDump(event, anonymizedFields)).join("\n"); +} - return Object.fromEntries( - Object.entries(value).map(([key, val]) => { - const nextPath = [...path, key]; - const pathKey = nextPath.join("."); - if (fieldsToAnonymize.has(key) || fieldsToAnonymize.has(pathKey)) { - return [key, key]; - } - return [key, anonymizeValue(val, nextPath)]; - }) - ); +function anonymizeValue(value: any, path: string[], fieldsToAnonymize: Set): any { + if (value === null || typeof value !== "object") { + return value; } - return JSON.stringify(anonymizeValue(obj, []), null, 2); -} + if (Array.isArray(value)) { + return value.map((item, index) => anonymizeValue(item, [...path, String(index)], fieldsToAnonymize)); + } -export function createArrayDump(objects: any[], anonymizedFields: string[]): string { - return objects.map(event => createObjectDump(event, anonymizedFields)).join("\n"); + return Object.fromEntries( + Object.entries(value).map(([key, val]) => { + const nextPath = [...path, key]; + const pathKey = nextPath.join("."); + if (fieldsToAnonymize.has(key) || fieldsToAnonymize.has(pathKey)) { + return [key, key]; + } + return [key, anonymizeValue(val, nextPath, fieldsToAnonymize)]; + }) + ); } /** diff --git a/src/app-server/ClientRequest.ts b/src/app-server/ClientRequest.ts index 5fa8f27b..b854afd6 100644 --- a/src/app-server/ClientRequest.ts +++ b/src/app-server/ClientRequest.ts @@ -20,6 +20,13 @@ import type { ExperimentalFeatureListParams } from "./v2/ExperimentalFeatureList import type { ExternalAgentConfigDetectParams } from "./v2/ExternalAgentConfigDetectParams"; import type { ExternalAgentConfigImportParams } from "./v2/ExternalAgentConfigImportParams"; import type { FeedbackUploadParams } from "./v2/FeedbackUploadParams"; +import type { FsCopyParams } from "./v2/FsCopyParams"; +import type { FsCreateDirectoryParams } from "./v2/FsCreateDirectoryParams"; +import type { FsGetMetadataParams } from "./v2/FsGetMetadataParams"; +import type { FsReadDirectoryParams } from "./v2/FsReadDirectoryParams"; +import type { FsReadFileParams } from "./v2/FsReadFileParams"; +import type { FsRemoveParams } from "./v2/FsRemoveParams"; +import type { FsWriteFileParams } from "./v2/FsWriteFileParams"; import type { GetAccountParams } from "./v2/GetAccountParams"; import type { ListMcpServerStatusParams } from "./v2/ListMcpServerStatusParams"; import type { LoginAccountParams } from "./v2/LoginAccountParams"; @@ -27,12 +34,11 @@ import type { McpServerOauthLoginParams } from "./v2/McpServerOauthLoginParams"; import type { ModelListParams } from "./v2/ModelListParams"; import type { PluginInstallParams } from "./v2/PluginInstallParams"; import type { PluginListParams } from "./v2/PluginListParams"; +import type { PluginReadParams } from "./v2/PluginReadParams"; import type { PluginUninstallParams } from "./v2/PluginUninstallParams"; import type { ReviewStartParams } from "./v2/ReviewStartParams"; import type { SkillsConfigWriteParams } from "./v2/SkillsConfigWriteParams"; import type { SkillsListParams } from "./v2/SkillsListParams"; -import type { SkillsRemoteReadParams } from "./v2/SkillsRemoteReadParams"; -import type { SkillsRemoteWriteParams } from "./v2/SkillsRemoteWriteParams"; import type { ThreadArchiveParams } from "./v2/ThreadArchiveParams"; import type { ThreadCompactStartParams } from "./v2/ThreadCompactStartParams"; import type { ThreadForkParams } from "./v2/ThreadForkParams"; @@ -54,4 +60,4 @@ import type { WindowsSandboxSetupStartParams } from "./v2/WindowsSandboxSetupSta /** * Request from the client to the server. */ -export type ClientRequest ={ "method": "initialize", id: RequestId, params: InitializeParams, } | { "method": "thread/start", id: RequestId, params: ThreadStartParams, } | { "method": "thread/resume", id: RequestId, params: ThreadResumeParams, } | { "method": "thread/fork", id: RequestId, params: ThreadForkParams, } | { "method": "thread/archive", id: RequestId, params: ThreadArchiveParams, } | { "method": "thread/unsubscribe", id: RequestId, params: ThreadUnsubscribeParams, } | { "method": "thread/name/set", id: RequestId, params: ThreadSetNameParams, } | { "method": "thread/metadata/update", id: RequestId, params: ThreadMetadataUpdateParams, } | { "method": "thread/unarchive", id: RequestId, params: ThreadUnarchiveParams, } | { "method": "thread/compact/start", id: RequestId, params: ThreadCompactStartParams, } | { "method": "thread/rollback", id: RequestId, params: ThreadRollbackParams, } | { "method": "thread/list", id: RequestId, params: ThreadListParams, } | { "method": "thread/loaded/list", id: RequestId, params: ThreadLoadedListParams, } | { "method": "thread/read", id: RequestId, params: ThreadReadParams, } | { "method": "skills/list", id: RequestId, params: SkillsListParams, } | { "method": "plugin/list", id: RequestId, params: PluginListParams, } | { "method": "skills/remote/list", id: RequestId, params: SkillsRemoteReadParams, } | { "method": "skills/remote/export", id: RequestId, params: SkillsRemoteWriteParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "method": "plugin/install", id: RequestId, params: PluginInstallParams, } | { "method": "plugin/uninstall", id: RequestId, params: PluginUninstallParams, } | { "method": "turn/start", id: RequestId, params: TurnStartParams, } | { "method": "turn/steer", id: RequestId, params: TurnSteerParams, } | { "method": "turn/interrupt", id: RequestId, params: TurnInterruptParams, } | { "method": "review/start", id: RequestId, params: ReviewStartParams, } | { "method": "model/list", id: RequestId, params: ModelListParams, } | { "method": "experimentalFeature/list", id: RequestId, params: ExperimentalFeatureListParams, } | { "method": "mcpServer/oauth/login", id: RequestId, params: McpServerOauthLoginParams, } | { "method": "config/mcpServer/reload", id: RequestId, params: undefined, } | { "method": "mcpServerStatus/list", id: RequestId, params: ListMcpServerStatusParams, } | { "method": "windowsSandbox/setupStart", id: RequestId, params: WindowsSandboxSetupStartParams, } | { "method": "account/login/start", id: RequestId, params: LoginAccountParams, } | { "method": "account/login/cancel", id: RequestId, params: CancelLoginAccountParams, } | { "method": "account/logout", id: RequestId, params: undefined, } | { "method": "account/rateLimits/read", id: RequestId, params: undefined, } | { "method": "feedback/upload", id: RequestId, params: FeedbackUploadParams, } | { "method": "command/exec", id: RequestId, params: CommandExecParams, } | { "method": "command/exec/write", id: RequestId, params: CommandExecWriteParams, } | { "method": "command/exec/terminate", id: RequestId, params: CommandExecTerminateParams, } | { "method": "command/exec/resize", id: RequestId, params: CommandExecResizeParams, } | { "method": "config/read", id: RequestId, params: ConfigReadParams, } | { "method": "externalAgentConfig/detect", id: RequestId, params: ExternalAgentConfigDetectParams, } | { "method": "externalAgentConfig/import", id: RequestId, params: ExternalAgentConfigImportParams, } | { "method": "config/value/write", id: RequestId, params: ConfigValueWriteParams, } | { "method": "config/batchWrite", id: RequestId, params: ConfigBatchWriteParams, } | { "method": "configRequirements/read", id: RequestId, params: undefined, } | { "method": "account/read", id: RequestId, params: GetAccountParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "getAuthStatus", id: RequestId, params: GetAuthStatusParams, } | { "method": "fuzzyFileSearch", id: RequestId, params: FuzzyFileSearchParams, }; +export type ClientRequest ={ "method": "initialize", id: RequestId, params: InitializeParams, } | { "method": "thread/start", id: RequestId, params: ThreadStartParams, } | { "method": "thread/resume", id: RequestId, params: ThreadResumeParams, } | { "method": "thread/fork", id: RequestId, params: ThreadForkParams, } | { "method": "thread/archive", id: RequestId, params: ThreadArchiveParams, } | { "method": "thread/unsubscribe", id: RequestId, params: ThreadUnsubscribeParams, } | { "method": "thread/name/set", id: RequestId, params: ThreadSetNameParams, } | { "method": "thread/metadata/update", id: RequestId, params: ThreadMetadataUpdateParams, } | { "method": "thread/unarchive", id: RequestId, params: ThreadUnarchiveParams, } | { "method": "thread/compact/start", id: RequestId, params: ThreadCompactStartParams, } | { "method": "thread/rollback", id: RequestId, params: ThreadRollbackParams, } | { "method": "thread/list", id: RequestId, params: ThreadListParams, } | { "method": "thread/loaded/list", id: RequestId, params: ThreadLoadedListParams, } | { "method": "thread/read", id: RequestId, params: ThreadReadParams, } | { "method": "skills/list", id: RequestId, params: SkillsListParams, } | { "method": "plugin/list", id: RequestId, params: PluginListParams, } | { "method": "plugin/read", id: RequestId, params: PluginReadParams, } | { "method": "app/list", id: RequestId, params: AppsListParams, } | { "method": "fs/readFile", id: RequestId, params: FsReadFileParams, } | { "method": "fs/writeFile", id: RequestId, params: FsWriteFileParams, } | { "method": "fs/createDirectory", id: RequestId, params: FsCreateDirectoryParams, } | { "method": "fs/getMetadata", id: RequestId, params: FsGetMetadataParams, } | { "method": "fs/readDirectory", id: RequestId, params: FsReadDirectoryParams, } | { "method": "fs/remove", id: RequestId, params: FsRemoveParams, } | { "method": "fs/copy", id: RequestId, params: FsCopyParams, } | { "method": "skills/config/write", id: RequestId, params: SkillsConfigWriteParams, } | { "method": "plugin/install", id: RequestId, params: PluginInstallParams, } | { "method": "plugin/uninstall", id: RequestId, params: PluginUninstallParams, } | { "method": "turn/start", id: RequestId, params: TurnStartParams, } | { "method": "turn/steer", id: RequestId, params: TurnSteerParams, } | { "method": "turn/interrupt", id: RequestId, params: TurnInterruptParams, } | { "method": "review/start", id: RequestId, params: ReviewStartParams, } | { "method": "model/list", id: RequestId, params: ModelListParams, } | { "method": "experimentalFeature/list", id: RequestId, params: ExperimentalFeatureListParams, } | { "method": "mcpServer/oauth/login", id: RequestId, params: McpServerOauthLoginParams, } | { "method": "config/mcpServer/reload", id: RequestId, params: undefined, } | { "method": "mcpServerStatus/list", id: RequestId, params: ListMcpServerStatusParams, } | { "method": "windowsSandbox/setupStart", id: RequestId, params: WindowsSandboxSetupStartParams, } | { "method": "account/login/start", id: RequestId, params: LoginAccountParams, } | { "method": "account/login/cancel", id: RequestId, params: CancelLoginAccountParams, } | { "method": "account/logout", id: RequestId, params: undefined, } | { "method": "account/rateLimits/read", id: RequestId, params: undefined, } | { "method": "feedback/upload", id: RequestId, params: FeedbackUploadParams, } | { "method": "command/exec", id: RequestId, params: CommandExecParams, } | { "method": "command/exec/write", id: RequestId, params: CommandExecWriteParams, } | { "method": "command/exec/terminate", id: RequestId, params: CommandExecTerminateParams, } | { "method": "command/exec/resize", id: RequestId, params: CommandExecResizeParams, } | { "method": "config/read", id: RequestId, params: ConfigReadParams, } | { "method": "externalAgentConfig/detect", id: RequestId, params: ExternalAgentConfigDetectParams, } | { "method": "externalAgentConfig/import", id: RequestId, params: ExternalAgentConfigImportParams, } | { "method": "config/value/write", id: RequestId, params: ConfigValueWriteParams, } | { "method": "config/batchWrite", id: RequestId, params: ConfigBatchWriteParams, } | { "method": "configRequirements/read", id: RequestId, params: undefined, } | { "method": "account/read", id: RequestId, params: GetAccountParams, } | { "method": "getConversationSummary", id: RequestId, params: GetConversationSummaryParams, } | { "method": "gitDiffToRemote", id: RequestId, params: GitDiffToRemoteParams, } | { "method": "getAuthStatus", id: RequestId, params: GetAuthStatusParams, } | { "method": "fuzzyFileSearch", id: RequestId, params: FuzzyFileSearchParams, }; diff --git a/src/app-server/InitializeCapabilities.ts b/src/app-server/InitializeCapabilities.ts index a6ac24ef..125b4b1f 100644 --- a/src/app-server/InitializeCapabilities.ts +++ b/src/app-server/InitializeCapabilities.ts @@ -12,6 +12,6 @@ export type InitializeCapabilities = { experimentalApi: boolean, /** * Exact notification method names that should be suppressed for this - * connection (for example `codex/event/session_configured`). + * connection (for example `thread/started`). */ optOutNotificationMethods?: Array | null, }; diff --git a/src/app-server/InitializeResponse.ts b/src/app-server/InitializeResponse.ts index 8a6bec66..47978fc8 100644 --- a/src/app-server/InitializeResponse.ts +++ b/src/app-server/InitializeResponse.ts @@ -2,4 +2,14 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type InitializeResponse = { userAgent: string, }; +export type InitializeResponse = { userAgent: string, +/** + * Platform family for the running app-server target, for example + * `"unix"` or `"windows"`. + */ +platformFamily: string, +/** + * Operating system for the running app-server target, for example + * `"macos"`, `"linux"`, or `"windows"`. + */ +platformOs: string, }; diff --git a/src/app-server/MacOsContactsPermission.ts b/src/app-server/MacOsContactsPermission.ts new file mode 100644 index 00000000..dd6d7b59 --- /dev/null +++ b/src/app-server/MacOsContactsPermission.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MacOsContactsPermission = "none" | "read_only" | "read_write"; diff --git a/src/app-server/RealtimeConversationVersion.ts b/src/app-server/RealtimeConversationVersion.ts new file mode 100644 index 00000000..cedc4bbe --- /dev/null +++ b/src/app-server/RealtimeConversationVersion.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type RealtimeConversationVersion = "v1" | "v2"; diff --git a/src/app-server/ResponseItem.ts b/src/app-server/ResponseItem.ts index dc424859..e9ab2a84 100644 --- a/src/app-server/ResponseItem.ts +++ b/src/app-server/ResponseItem.ts @@ -2,7 +2,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ContentItem } from "./ContentItem"; -import type { FunctionCallOutputPayload } from "./FunctionCallOutputPayload"; +import type { FunctionCallOutputBody } from "./FunctionCallOutputBody"; import type { GhostCommit } from "./GhostCommit"; import type { LocalShellAction } from "./LocalShellAction"; import type { LocalShellStatus } from "./LocalShellStatus"; @@ -15,4 +15,4 @@ export type ResponseItem = { "type": "message", role: string, content: Array | { [key in string]?: JsonValue } | null }); +export type Config = {model: string | null, review_model: string | null, model_context_window: bigint | null, model_auto_compact_token_limit: bigint | null, model_provider: string | null, approval_policy: AskForApproval | null, /** + * [UNSTABLE] Optional default for where approval requests are routed for + * review. + */ +approvals_reviewer: ApprovalsReviewer | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: string | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, service_tier: ServiceTier | null, analytics: AnalyticsConfig | null} & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); diff --git a/src/app-server/v2/DynamicToolSpec.ts b/src/app-server/v2/DynamicToolSpec.ts index 8b39793f..18596e31 100644 --- a/src/app-server/v2/DynamicToolSpec.ts +++ b/src/app-server/v2/DynamicToolSpec.ts @@ -3,4 +3,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { JsonValue } from "../serde_json/JsonValue"; -export type DynamicToolSpec = { name: string, description: string, inputSchema: JsonValue, }; +export type DynamicToolSpec = { name: string, description: string, inputSchema: JsonValue, deferLoading?: boolean, }; diff --git a/src/app-server/v2/FsCopyParams.ts b/src/app-server/v2/FsCopyParams.ts new file mode 100644 index 00000000..8f1ec8d4 --- /dev/null +++ b/src/app-server/v2/FsCopyParams.ts @@ -0,0 +1,21 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Copy a file or directory tree on the host filesystem. + */ +export type FsCopyParams = { +/** + * Absolute source path. + */ +sourcePath: AbsolutePathBuf, +/** + * Absolute destination path. + */ +destinationPath: AbsolutePathBuf, +/** + * Required for directory copies; ignored for file copies. + */ +recursive?: boolean, }; diff --git a/src/app-server/v2/FsCopyResponse.ts b/src/app-server/v2/FsCopyResponse.ts new file mode 100644 index 00000000..3e3061a8 --- /dev/null +++ b/src/app-server/v2/FsCopyResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Successful response for `fs/copy`. + */ +export type FsCopyResponse = Record; diff --git a/src/app-server/v2/FsCreateDirectoryParams.ts b/src/app-server/v2/FsCreateDirectoryParams.ts new file mode 100644 index 00000000..2afc9950 --- /dev/null +++ b/src/app-server/v2/FsCreateDirectoryParams.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Create a directory on the host filesystem. + */ +export type FsCreateDirectoryParams = { +/** + * Absolute directory path to create. + */ +path: AbsolutePathBuf, +/** + * Whether parent directories should also be created. Defaults to `true`. + */ +recursive?: boolean | null, }; diff --git a/src/app-server/v2/FsCreateDirectoryResponse.ts b/src/app-server/v2/FsCreateDirectoryResponse.ts new file mode 100644 index 00000000..5d251b71 --- /dev/null +++ b/src/app-server/v2/FsCreateDirectoryResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Successful response for `fs/createDirectory`. + */ +export type FsCreateDirectoryResponse = Record; diff --git a/src/app-server/v2/FsGetMetadataParams.ts b/src/app-server/v2/FsGetMetadataParams.ts new file mode 100644 index 00000000..38e46c7b --- /dev/null +++ b/src/app-server/v2/FsGetMetadataParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Request metadata for an absolute path. + */ +export type FsGetMetadataParams = { +/** + * Absolute path to inspect. + */ +path: AbsolutePathBuf, }; diff --git a/src/app-server/v2/FsGetMetadataResponse.ts b/src/app-server/v2/FsGetMetadataResponse.ts new file mode 100644 index 00000000..351c6462 --- /dev/null +++ b/src/app-server/v2/FsGetMetadataResponse.ts @@ -0,0 +1,24 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Metadata returned by `fs/getMetadata`. + */ +export type FsGetMetadataResponse = { +/** + * Whether the path currently resolves to a directory. + */ +isDirectory: boolean, +/** + * Whether the path currently resolves to a regular file. + */ +isFile: boolean, +/** + * File creation time in Unix milliseconds when available, otherwise `0`. + */ +createdAtMs: number, +/** + * File modification time in Unix milliseconds when available, otherwise `0`. + */ +modifiedAtMs: number, }; diff --git a/src/app-server/v2/FsReadDirectoryEntry.ts b/src/app-server/v2/FsReadDirectoryEntry.ts new file mode 100644 index 00000000..2696d7a4 --- /dev/null +++ b/src/app-server/v2/FsReadDirectoryEntry.ts @@ -0,0 +1,20 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * A directory entry returned by `fs/readDirectory`. + */ +export type FsReadDirectoryEntry = { +/** + * Direct child entry name only, not an absolute or relative path. + */ +fileName: string, +/** + * Whether this entry resolves to a directory. + */ +isDirectory: boolean, +/** + * Whether this entry resolves to a regular file. + */ +isFile: boolean, }; diff --git a/src/app-server/v2/FsReadDirectoryParams.ts b/src/app-server/v2/FsReadDirectoryParams.ts new file mode 100644 index 00000000..770eea3a --- /dev/null +++ b/src/app-server/v2/FsReadDirectoryParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * List direct child names for a directory. + */ +export type FsReadDirectoryParams = { +/** + * Absolute directory path to read. + */ +path: AbsolutePathBuf, }; diff --git a/src/app-server/v2/FsReadDirectoryResponse.ts b/src/app-server/v2/FsReadDirectoryResponse.ts new file mode 100644 index 00000000..878e858f --- /dev/null +++ b/src/app-server/v2/FsReadDirectoryResponse.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { FsReadDirectoryEntry } from "./FsReadDirectoryEntry"; + +/** + * Directory entries returned by `fs/readDirectory`. + */ +export type FsReadDirectoryResponse = { +/** + * Direct child entries in the requested directory. + */ +entries: Array, }; diff --git a/src/app-server/v2/FsReadFileParams.ts b/src/app-server/v2/FsReadFileParams.ts new file mode 100644 index 00000000..f389b44f --- /dev/null +++ b/src/app-server/v2/FsReadFileParams.ts @@ -0,0 +1,13 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Read a file from the host filesystem. + */ +export type FsReadFileParams = { +/** + * Absolute path to read. + */ +path: AbsolutePathBuf, }; diff --git a/src/app-server/v2/FsReadFileResponse.ts b/src/app-server/v2/FsReadFileResponse.ts new file mode 100644 index 00000000..075d1269 --- /dev/null +++ b/src/app-server/v2/FsReadFileResponse.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Base64-encoded file contents returned by `fs/readFile`. + */ +export type FsReadFileResponse = { +/** + * File contents encoded as base64. + */ +dataBase64: string, }; diff --git a/src/app-server/v2/FsRemoveParams.ts b/src/app-server/v2/FsRemoveParams.ts new file mode 100644 index 00000000..c9f02eb0 --- /dev/null +++ b/src/app-server/v2/FsRemoveParams.ts @@ -0,0 +1,21 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Remove a file or directory tree from the host filesystem. + */ +export type FsRemoveParams = { +/** + * Absolute path to remove. + */ +path: AbsolutePathBuf, +/** + * Whether directory removal should recurse. Defaults to `true`. + */ +recursive?: boolean | null, +/** + * Whether missing paths should be ignored. Defaults to `true`. + */ +force?: boolean | null, }; diff --git a/src/app-server/v2/FsRemoveResponse.ts b/src/app-server/v2/FsRemoveResponse.ts new file mode 100644 index 00000000..981c28fa --- /dev/null +++ b/src/app-server/v2/FsRemoveResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Successful response for `fs/remove`. + */ +export type FsRemoveResponse = Record; diff --git a/src/app-server/v2/FsWriteFileParams.ts b/src/app-server/v2/FsWriteFileParams.ts new file mode 100644 index 00000000..7c22abdb --- /dev/null +++ b/src/app-server/v2/FsWriteFileParams.ts @@ -0,0 +1,17 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +/** + * Write a file on the host filesystem. + */ +export type FsWriteFileParams = { +/** + * Absolute path to write. + */ +path: AbsolutePathBuf, +/** + * File contents encoded as base64. + */ +dataBase64: string, }; diff --git a/src/app-server/v2/FsWriteFileResponse.ts b/src/app-server/v2/FsWriteFileResponse.ts new file mode 100644 index 00000000..ad0ce283 --- /dev/null +++ b/src/app-server/v2/FsWriteFileResponse.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Successful response for `fs/writeFile`. + */ +export type FsWriteFileResponse = Record; diff --git a/src/app-server/v2/GrantedPermissionProfile.ts b/src/app-server/v2/GrantedPermissionProfile.ts index 84a9aa37..3ae6c605 100644 --- a/src/app-server/v2/GrantedPermissionProfile.ts +++ b/src/app-server/v2/GrantedPermissionProfile.ts @@ -3,6 +3,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { AdditionalFileSystemPermissions } from "./AdditionalFileSystemPermissions"; import type { AdditionalNetworkPermissions } from "./AdditionalNetworkPermissions"; -import type { GrantedMacOsPermissions } from "./GrantedMacOsPermissions"; -export type GrantedPermissionProfile = { network?: AdditionalNetworkPermissions, fileSystem?: AdditionalFileSystemPermissions, macos?: GrantedMacOsPermissions, }; +export type GrantedPermissionProfile = { network?: AdditionalNetworkPermissions, fileSystem?: AdditionalFileSystemPermissions, }; diff --git a/src/app-server/v2/GuardianApprovalReview.ts b/src/app-server/v2/GuardianApprovalReview.ts new file mode 100644 index 00000000..e26282be --- /dev/null +++ b/src/app-server/v2/GuardianApprovalReview.ts @@ -0,0 +1,12 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { GuardianApprovalReviewStatus } from "./GuardianApprovalReviewStatus"; +import type { GuardianRiskLevel } from "./GuardianRiskLevel"; + +/** + * [UNSTABLE] Temporary guardian approval review payload used by + * `item/autoApprovalReview/*` notifications. This shape is expected to change + * soon. + */ +export type GuardianApprovalReview = { status: GuardianApprovalReviewStatus, riskScore: number | null, riskLevel: GuardianRiskLevel | null, rationale: string | null, }; diff --git a/src/app-server/v2/GuardianApprovalReviewStatus.ts b/src/app-server/v2/GuardianApprovalReviewStatus.ts new file mode 100644 index 00000000..b98578b2 --- /dev/null +++ b/src/app-server/v2/GuardianApprovalReviewStatus.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * [UNSTABLE] Lifecycle state for a guardian approval review. + */ +export type GuardianApprovalReviewStatus = "inProgress" | "approved" | "denied" | "aborted"; diff --git a/src/app-server/v2/GuardianRiskLevel.ts b/src/app-server/v2/GuardianRiskLevel.ts new file mode 100644 index 00000000..1b0a945f --- /dev/null +++ b/src/app-server/v2/GuardianRiskLevel.ts @@ -0,0 +1,8 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * [UNSTABLE] Risk level assigned by guardian approval review. + */ +export type GuardianRiskLevel = "low" | "medium" | "high"; diff --git a/src/app-server/v2/HookEventName.ts b/src/app-server/v2/HookEventName.ts index d07429a9..a531b78d 100644 --- a/src/app-server/v2/HookEventName.ts +++ b/src/app-server/v2/HookEventName.ts @@ -2,4 +2,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type HookEventName = "sessionStart" | "stop"; +export type HookEventName = "sessionStart" | "userPromptSubmit" | "stop"; diff --git a/src/app-server/v2/ItemGuardianApprovalReviewCompletedNotification.ts b/src/app-server/v2/ItemGuardianApprovalReviewCompletedNotification.ts new file mode 100644 index 00000000..ac4ae1b7 --- /dev/null +++ b/src/app-server/v2/ItemGuardianApprovalReviewCompletedNotification.ts @@ -0,0 +1,15 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; +import type { GuardianApprovalReview } from "./GuardianApprovalReview"; + +/** + * [UNSTABLE] Temporary notification payload for guardian automatic approval + * review. This shape is expected to change soon. + * + * TODO(ccunningham): Attach guardian review state to the reviewed tool item's + * lifecycle instead of sending separate standalone review notifications so the + * app-server API can persist and replay review state via `thread/read`. + */ +export type ItemGuardianApprovalReviewCompletedNotification = { threadId: string, turnId: string, targetItemId: string, review: GuardianApprovalReview, action: JsonValue | null, }; diff --git a/src/app-server/v2/ItemGuardianApprovalReviewStartedNotification.ts b/src/app-server/v2/ItemGuardianApprovalReviewStartedNotification.ts new file mode 100644 index 00000000..b2296268 --- /dev/null +++ b/src/app-server/v2/ItemGuardianApprovalReviewStartedNotification.ts @@ -0,0 +1,15 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { JsonValue } from "../serde_json/JsonValue"; +import type { GuardianApprovalReview } from "./GuardianApprovalReview"; + +/** + * [UNSTABLE] Temporary notification payload for guardian automatic approval + * review. This shape is expected to change soon. + * + * TODO(ccunningham): Attach guardian review state to the reviewed tool item's + * lifecycle instead of sending separate standalone review notifications so the + * app-server API can persist and replay review state via `thread/read`. + */ +export type ItemGuardianApprovalReviewStartedNotification = { threadId: string, turnId: string, targetItemId: string, review: GuardianApprovalReview, action: JsonValue | null, }; diff --git a/src/app-server/v2/MarketplaceInterface.ts b/src/app-server/v2/MarketplaceInterface.ts new file mode 100644 index 00000000..f82dc179 --- /dev/null +++ b/src/app-server/v2/MarketplaceInterface.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MarketplaceInterface = { displayName: string | null, }; diff --git a/src/app-server/v2/MemoryCitation.ts b/src/app-server/v2/MemoryCitation.ts new file mode 100644 index 00000000..7657e29f --- /dev/null +++ b/src/app-server/v2/MemoryCitation.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { MemoryCitationEntry } from "./MemoryCitationEntry"; + +export type MemoryCitation = { entries: Array, threadIds: Array, }; diff --git a/src/app-server/v2/MemoryCitationEntry.ts b/src/app-server/v2/MemoryCitationEntry.ts new file mode 100644 index 00000000..9b9ce172 --- /dev/null +++ b/src/app-server/v2/MemoryCitationEntry.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type MemoryCitationEntry = { path: string, lineStart: number, lineEnd: number, note: string, }; diff --git a/src/app-server/v2/PermissionsRequestApprovalParams.ts b/src/app-server/v2/PermissionsRequestApprovalParams.ts index cde277f1..efdefead 100644 --- a/src/app-server/v2/PermissionsRequestApprovalParams.ts +++ b/src/app-server/v2/PermissionsRequestApprovalParams.ts @@ -1,6 +1,6 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { AdditionalPermissionProfile } from "./AdditionalPermissionProfile"; +import type { RequestPermissionProfile } from "./RequestPermissionProfile"; -export type PermissionsRequestApprovalParams = { threadId: string, turnId: string, itemId: string, reason: string | null, permissions: AdditionalPermissionProfile, }; +export type PermissionsRequestApprovalParams = { threadId: string, turnId: string, itemId: string, reason: string | null, permissions: RequestPermissionProfile, }; diff --git a/src/app-server/v2/PluginAuthPolicy.ts b/src/app-server/v2/PluginAuthPolicy.ts new file mode 100644 index 00000000..5b90e9c3 --- /dev/null +++ b/src/app-server/v2/PluginAuthPolicy.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginAuthPolicy = "ON_INSTALL" | "ON_USE"; diff --git a/src/app-server/v2/PluginDetail.ts b/src/app-server/v2/PluginDetail.ts new file mode 100644 index 00000000..4bfd35fe --- /dev/null +++ b/src/app-server/v2/PluginDetail.ts @@ -0,0 +1,9 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { AppSummary } from "./AppSummary"; +import type { PluginSummary } from "./PluginSummary"; +import type { SkillSummary } from "./SkillSummary"; + +export type PluginDetail = { marketplaceName: string, marketplacePath: AbsolutePathBuf, summary: PluginSummary, description: string | null, skills: Array, apps: Array, mcpServers: Array, }; diff --git a/src/app-server/v2/PluginInstallParams.ts b/src/app-server/v2/PluginInstallParams.ts index 86326b13..190dee04 100644 --- a/src/app-server/v2/PluginInstallParams.ts +++ b/src/app-server/v2/PluginInstallParams.ts @@ -3,4 +3,8 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { AbsolutePathBuf } from "../AbsolutePathBuf"; -export type PluginInstallParams = { marketplacePath: AbsolutePathBuf, pluginName: string, }; +export type PluginInstallParams = { marketplacePath: AbsolutePathBuf, pluginName: string, +/** + * When true, apply the remote plugin change before the local install flow. + */ +forceRemoteSync?: boolean, }; diff --git a/src/app-server/v2/PluginInstallPolicy.ts b/src/app-server/v2/PluginInstallPolicy.ts new file mode 100644 index 00000000..d624f38e --- /dev/null +++ b/src/app-server/v2/PluginInstallPolicy.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PluginInstallPolicy = "NOT_AVAILABLE" | "AVAILABLE" | "INSTALLED_BY_DEFAULT"; diff --git a/src/app-server/v2/PluginInstallResponse.ts b/src/app-server/v2/PluginInstallResponse.ts index 08c61f37..b88119d4 100644 --- a/src/app-server/v2/PluginInstallResponse.ts +++ b/src/app-server/v2/PluginInstallResponse.ts @@ -2,5 +2,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { AppSummary } from "./AppSummary"; +import type { PluginAuthPolicy } from "./PluginAuthPolicy"; -export type PluginInstallResponse = { appsNeedingAuth: Array, }; +export type PluginInstallResponse = { authPolicy: PluginAuthPolicy, appsNeedingAuth: Array, }; diff --git a/src/app-server/v2/PluginInterface.ts b/src/app-server/v2/PluginInterface.ts index f9f016d0..cea42d29 100644 --- a/src/app-server/v2/PluginInterface.ts +++ b/src/app-server/v2/PluginInterface.ts @@ -3,4 +3,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { AbsolutePathBuf } from "../AbsolutePathBuf"; -export type PluginInterface = { displayName: string | null, shortDescription: string | null, longDescription: string | null, developerName: string | null, category: string | null, capabilities: Array, websiteUrl: string | null, privacyPolicyUrl: string | null, termsOfServiceUrl: string | null, defaultPrompt: string | null, brandColor: string | null, composerIcon: AbsolutePathBuf | null, logo: AbsolutePathBuf | null, screenshots: Array, }; +export type PluginInterface = { displayName: string | null, shortDescription: string | null, longDescription: string | null, developerName: string | null, category: string | null, capabilities: Array, websiteUrl: string | null, privacyPolicyUrl: string | null, termsOfServiceUrl: string | null, +/** + * Starter prompts for the plugin. Capped at 3 entries with a maximum of + * 128 characters per entry. + */ +defaultPrompt: Array | null, brandColor: string | null, composerIcon: AbsolutePathBuf | null, logo: AbsolutePathBuf | null, screenshots: Array, }; diff --git a/src/app-server/v2/PluginListParams.ts b/src/app-server/v2/PluginListParams.ts index 078feca2..07ecee5e 100644 --- a/src/app-server/v2/PluginListParams.ts +++ b/src/app-server/v2/PluginListParams.ts @@ -8,4 +8,9 @@ export type PluginListParams = { * Optional working directories used to discover repo marketplaces. When omitted, * only home-scoped marketplaces and the official curated marketplace are considered. */ -cwds?: Array | null, }; +cwds?: Array | null, +/** + * When true, reconcile the official curated marketplace against the remote plugin state + * before listing marketplaces. + */ +forceRemoteSync?: boolean, }; diff --git a/src/app-server/v2/PluginListResponse.ts b/src/app-server/v2/PluginListResponse.ts index 7c3cc692..c6de9e7e 100644 --- a/src/app-server/v2/PluginListResponse.ts +++ b/src/app-server/v2/PluginListResponse.ts @@ -3,4 +3,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { PluginMarketplaceEntry } from "./PluginMarketplaceEntry"; -export type PluginListResponse = { marketplaces: Array, }; +export type PluginListResponse = { marketplaces: Array, remoteSyncError: string | null, }; diff --git a/src/app-server/v2/PluginMarketplaceEntry.ts b/src/app-server/v2/PluginMarketplaceEntry.ts index 32042d1d..c0ab75b8 100644 --- a/src/app-server/v2/PluginMarketplaceEntry.ts +++ b/src/app-server/v2/PluginMarketplaceEntry.ts @@ -2,6 +2,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { AbsolutePathBuf } from "../AbsolutePathBuf"; +import type { MarketplaceInterface } from "./MarketplaceInterface"; import type { PluginSummary } from "./PluginSummary"; -export type PluginMarketplaceEntry = { name: string, path: AbsolutePathBuf, plugins: Array, }; +export type PluginMarketplaceEntry = { name: string, path: AbsolutePathBuf, interface: MarketplaceInterface | null, plugins: Array, }; diff --git a/src/app-server/v2/PluginReadParams.ts b/src/app-server/v2/PluginReadParams.ts new file mode 100644 index 00000000..cd669687 --- /dev/null +++ b/src/app-server/v2/PluginReadParams.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AbsolutePathBuf } from "../AbsolutePathBuf"; + +export type PluginReadParams = { marketplacePath: AbsolutePathBuf, pluginName: string, }; diff --git a/src/app-server/v2/PluginReadResponse.ts b/src/app-server/v2/PluginReadResponse.ts new file mode 100644 index 00000000..841b916e --- /dev/null +++ b/src/app-server/v2/PluginReadResponse.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginDetail } from "./PluginDetail"; + +export type PluginReadResponse = { plugin: PluginDetail, }; diff --git a/src/app-server/v2/PluginSummary.ts b/src/app-server/v2/PluginSummary.ts index baefe10d..1eb443c5 100644 --- a/src/app-server/v2/PluginSummary.ts +++ b/src/app-server/v2/PluginSummary.ts @@ -1,7 +1,9 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginAuthPolicy } from "./PluginAuthPolicy"; +import type { PluginInstallPolicy } from "./PluginInstallPolicy"; import type { PluginInterface } from "./PluginInterface"; import type { PluginSource } from "./PluginSource"; -export type PluginSummary = { id: string, name: string, source: PluginSource, installed: boolean, enabled: boolean, interface: PluginInterface | null, }; +export type PluginSummary = { id: string, name: string, source: PluginSource, installed: boolean, enabled: boolean, installPolicy: PluginInstallPolicy, authPolicy: PluginAuthPolicy, interface: PluginInterface | null, }; diff --git a/src/app-server/v2/PluginUninstallParams.ts b/src/app-server/v2/PluginUninstallParams.ts index e7f52c0e..b92a21c9 100644 --- a/src/app-server/v2/PluginUninstallParams.ts +++ b/src/app-server/v2/PluginUninstallParams.ts @@ -2,4 +2,8 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type PluginUninstallParams = { pluginId: string, }; +export type PluginUninstallParams = { pluginId: string, +/** + * When true, apply the remote plugin change before the local uninstall flow. + */ +forceRemoteSync?: boolean, }; diff --git a/src/app-server/v2/ProfileV2.ts b/src/app-server/v2/ProfileV2.ts index f2c72b3a..7afe3e0c 100644 --- a/src/app-server/v2/ProfileV2.ts +++ b/src/app-server/v2/ProfileV2.ts @@ -7,7 +7,13 @@ import type { ServiceTier } from "../ServiceTier"; import type { Verbosity } from "../Verbosity"; import type { WebSearchMode } from "../WebSearchMode"; import type { JsonValue } from "../serde_json/JsonValue"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; import type { ToolsV2 } from "./ToolsV2"; -export type ProfileV2 = { model: string | null, model_provider: string | null, approval_policy: AskForApproval | null, service_tier: ServiceTier | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, chatgpt_base_url: string | null, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type ProfileV2 = {model: string | null, model_provider: string | null, approval_policy: AskForApproval | null, /** + * [UNSTABLE] Optional profile-level override for where approval requests + * are routed for review. If omitted, the enclosing config default is + * used. + */ +approvals_reviewer: ApprovalsReviewer | null, service_tier: ServiceTier | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, chatgpt_base_url: string | null} & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); diff --git a/src/app-server/v2/RequestPermissionProfile.ts b/src/app-server/v2/RequestPermissionProfile.ts new file mode 100644 index 00000000..2bf8d8df --- /dev/null +++ b/src/app-server/v2/RequestPermissionProfile.ts @@ -0,0 +1,7 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { AdditionalFileSystemPermissions } from "./AdditionalFileSystemPermissions"; +import type { AdditionalNetworkPermissions } from "./AdditionalNetworkPermissions"; + +export type RequestPermissionProfile = { network: AdditionalNetworkPermissions | null, fileSystem: AdditionalFileSystemPermissions | null, }; diff --git a/src/app-server/v2/SessionSource.ts b/src/app-server/v2/SessionSource.ts index b35b421f..852e6ded 100644 --- a/src/app-server/v2/SessionSource.ts +++ b/src/app-server/v2/SessionSource.ts @@ -3,4 +3,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { SubAgentSource } from "../SubAgentSource"; -export type SessionSource = "cli" | "vscode" | "exec" | "appServer" | { "subAgent": SubAgentSource } | "unknown"; +export type SessionSource = "cli" | "vscode" | "exec" | "appServer" | { "custom": string } | { "subAgent": SubAgentSource } | "unknown"; diff --git a/src/app-server/v2/SkillSummary.ts b/src/app-server/v2/SkillSummary.ts new file mode 100644 index 00000000..818e0b05 --- /dev/null +++ b/src/app-server/v2/SkillSummary.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SkillInterface } from "./SkillInterface"; + +export type SkillSummary = { name: string, description: string, shortDescription: string | null, interface: SkillInterface | null, path: string, }; diff --git a/src/app-server/v2/ThreadForkParams.ts b/src/app-server/v2/ThreadForkParams.ts index b071bc85..a7ba3118 100644 --- a/src/app-server/v2/ThreadForkParams.ts +++ b/src/app-server/v2/ThreadForkParams.ts @@ -3,6 +3,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ServiceTier } from "../ServiceTier"; import type { JsonValue } from "../serde_json/JsonValue"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxMode } from "./SandboxMode"; @@ -22,7 +23,11 @@ export type ThreadForkParams = {threadId: string, /** path?: string | null, /** * Configuration overrides for the forked thread, if any. */ -model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, /** +model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, /** + * Override where approval requests are routed for review on this thread + * and subsequent turns. + */ +approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, ephemeral?: boolean, /** * If true, persist additional rollout EventMsg variants required to * reconstruct a richer thread history on subsequent resume/fork/read. */ diff --git a/src/app-server/v2/ThreadForkResponse.ts b/src/app-server/v2/ThreadForkResponse.ts index 6125448c..00dade9f 100644 --- a/src/app-server/v2/ThreadForkResponse.ts +++ b/src/app-server/v2/ThreadForkResponse.ts @@ -3,8 +3,13 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ReasoningEffort } from "../ReasoningEffort"; import type { ServiceTier } from "../ServiceTier"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; -export type ThreadForkResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string, approvalPolicy: AskForApproval, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; +export type ThreadForkResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string, approvalPolicy: AskForApproval, +/** + * Reviewer currently used for approval requests on this thread. + */ +approvalsReviewer: ApprovalsReviewer, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; diff --git a/src/app-server/v2/ThreadItem.ts b/src/app-server/v2/ThreadItem.ts index 488c07e5..51ab9e88 100644 --- a/src/app-server/v2/ThreadItem.ts +++ b/src/app-server/v2/ThreadItem.ts @@ -2,6 +2,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { MessagePhase } from "../MessagePhase"; +import type { ReasoningEffort } from "../ReasoningEffort"; import type { JsonValue } from "../serde_json/JsonValue"; import type { CollabAgentState } from "./CollabAgentState"; import type { CollabAgentTool } from "./CollabAgentTool"; @@ -14,11 +15,12 @@ import type { FileUpdateChange } from "./FileUpdateChange"; import type { McpToolCallError } from "./McpToolCallError"; import type { McpToolCallResult } from "./McpToolCallResult"; import type { McpToolCallStatus } from "./McpToolCallStatus"; +import type { MemoryCitation } from "./MemoryCitation"; import type { PatchApplyStatus } from "./PatchApplyStatus"; import type { UserInput } from "./UserInput"; import type { WebSearchAction } from "./WebSearchAction"; -export type ThreadItem = { "type": "userMessage", id: string, content: Array, } | { "type": "agentMessage", id: string, text: string, phase: MessagePhase | null, } | { "type": "plan", id: string, text: string, } | { "type": "reasoning", id: string, summary: Array, content: Array, } | { "type": "commandExecution", id: string, +export type ThreadItem = { "type": "userMessage", id: string, content: Array, } | { "type": "agentMessage", id: string, text: string, phase: MessagePhase | null, memoryCitation: MemoryCitation | null, } | { "type": "plan", id: string, text: string, } | { "type": "reasoning", id: string, summary: Array, content: Array, } | { "type": "commandExecution", id: string, /** * The command to be executed. */ @@ -82,6 +84,14 @@ receiverThreadIds: Array, * Prompt text sent as part of the collab tool call, when available. */ prompt: string | null, +/** + * Model requested for the spawned agent, when applicable. + */ +model: string | null, +/** + * Reasoning effort requested for the spawned agent, when applicable. + */ +reasoningEffort: ReasoningEffort | null, /** * Last known status of the target agents, when available. */ diff --git a/src/app-server/v2/ThreadRealtimeAudioChunk.ts b/src/app-server/v2/ThreadRealtimeAudioChunk.ts index 078f6422..eefb79dd 100644 --- a/src/app-server/v2/ThreadRealtimeAudioChunk.ts +++ b/src/app-server/v2/ThreadRealtimeAudioChunk.ts @@ -5,4 +5,4 @@ /** * EXPERIMENTAL - thread realtime audio chunk. */ -export type ThreadRealtimeAudioChunk = { data: string, sampleRate: number, numChannels: number, samplesPerChannel: number | null, }; +export type ThreadRealtimeAudioChunk = { data: string, sampleRate: number, numChannels: number, samplesPerChannel: number | null, itemId: string | null, }; diff --git a/src/app-server/v2/ThreadRealtimeStartedNotification.ts b/src/app-server/v2/ThreadRealtimeStartedNotification.ts index 736ecde1..d4941006 100644 --- a/src/app-server/v2/ThreadRealtimeStartedNotification.ts +++ b/src/app-server/v2/ThreadRealtimeStartedNotification.ts @@ -1,8 +1,9 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { RealtimeConversationVersion } from "../RealtimeConversationVersion"; /** * EXPERIMENTAL - emitted when thread realtime startup is accepted. */ -export type ThreadRealtimeStartedNotification = { threadId: string, sessionId: string | null, }; +export type ThreadRealtimeStartedNotification = { threadId: string, sessionId: string | null, version: RealtimeConversationVersion, }; diff --git a/src/app-server/v2/ThreadResumeParams.ts b/src/app-server/v2/ThreadResumeParams.ts index cc12020b..770344de 100644 --- a/src/app-server/v2/ThreadResumeParams.ts +++ b/src/app-server/v2/ThreadResumeParams.ts @@ -5,6 +5,7 @@ import type { Personality } from "../Personality"; import type { ResponseItem } from "../ResponseItem"; import type { ServiceTier } from "../ServiceTier"; import type { JsonValue } from "../serde_json/JsonValue"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxMode } from "./SandboxMode"; @@ -31,7 +32,11 @@ history?: Array | null, /** path?: string | null, /** * Configuration overrides for the resumed thread, if any. */ -model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, /** +model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, /** + * Override where approval requests are routed for review on this thread + * and subsequent turns. + */ +approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, /** * If true, persist additional rollout EventMsg variants required to * reconstruct a richer thread history on subsequent resume/fork/read. */ diff --git a/src/app-server/v2/ThreadResumeResponse.ts b/src/app-server/v2/ThreadResumeResponse.ts index 91ca8241..ba70bd8f 100644 --- a/src/app-server/v2/ThreadResumeResponse.ts +++ b/src/app-server/v2/ThreadResumeResponse.ts @@ -3,8 +3,13 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ReasoningEffort } from "../ReasoningEffort"; import type { ServiceTier } from "../ServiceTier"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; -export type ThreadResumeResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string, approvalPolicy: AskForApproval, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; +export type ThreadResumeResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string, approvalPolicy: AskForApproval, +/** + * Reviewer currently used for approval requests on this thread. + */ +approvalsReviewer: ApprovalsReviewer, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; diff --git a/src/app-server/v2/ThreadSourceKind.ts b/src/app-server/v2/ThreadSourceKind.ts index 0a464e3d..6ff9160e 100644 --- a/src/app-server/v2/ThreadSourceKind.ts +++ b/src/app-server/v2/ThreadSourceKind.ts @@ -2,4 +2,4 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -export type ThreadSourceKind = "cli" | "vscode" | "exec" | "appServer" | "subAgent" | "subAgentReview" | "subAgentCompact" | "subAgentThreadSpawn" | "subAgentOther" | "unknown"; +export type ThreadSourceKind = "cli" | "vscode" | "exec" | "appServer" | "custom" | "subAgent" | "subAgentReview" | "subAgentCompact" | "subAgentThreadSpawn" | "subAgentOther" | "unknown"; diff --git a/src/app-server/v2/ThreadStartParams.ts b/src/app-server/v2/ThreadStartParams.ts index db73763e..61f501ad 100644 --- a/src/app-server/v2/ThreadStartParams.ts +++ b/src/app-server/v2/ThreadStartParams.ts @@ -4,10 +4,15 @@ import type { Personality } from "../Personality"; import type { ServiceTier } from "../ServiceTier"; import type { JsonValue } from "../serde_json/JsonValue"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxMode } from "./SandboxMode"; -export type ThreadStartParams = {model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, serviceName?: string | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, ephemeral?: boolean | null, /** +export type ThreadStartParams = {model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, /** + * Override where approval requests are routed for review on this thread + * and subsequent turns. + */ +approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, serviceName?: string | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, ephemeral?: boolean | null, /** * If true, opt into emitting raw Responses API items on the event stream. * This is for internal use only (e.g. Codex Cloud). */ diff --git a/src/app-server/v2/ThreadStartResponse.ts b/src/app-server/v2/ThreadStartResponse.ts index f4882ce6..ee97bdf4 100644 --- a/src/app-server/v2/ThreadStartResponse.ts +++ b/src/app-server/v2/ThreadStartResponse.ts @@ -3,8 +3,13 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ReasoningEffort } from "../ReasoningEffort"; import type { ServiceTier } from "../ServiceTier"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; -export type ThreadStartResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string, approvalPolicy: AskForApproval, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; +export type ThreadStartResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string, approvalPolicy: AskForApproval, +/** + * Reviewer currently used for approval requests on this thread. + */ +approvalsReviewer: ApprovalsReviewer, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; diff --git a/src/app-server/v2/TurnStartParams.ts b/src/app-server/v2/TurnStartParams.ts index b8bf7ea6..8f57a5e6 100644 --- a/src/app-server/v2/TurnStartParams.ts +++ b/src/app-server/v2/TurnStartParams.ts @@ -7,6 +7,7 @@ import type { ReasoningEffort } from "../ReasoningEffort"; import type { ReasoningSummary } from "../ReasoningSummary"; import type { ServiceTier } from "../ServiceTier"; import type { JsonValue } from "../serde_json/JsonValue"; +import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { UserInput } from "./UserInput"; @@ -18,6 +19,10 @@ cwd?: string | null, /** * Override the approval policy for this turn and subsequent turns. */ approvalPolicy?: AskForApproval | null, /** + * Override where approval requests are routed for review on this turn and + * subsequent turns. + */ +approvalsReviewer?: ApprovalsReviewer | null, /** * Override the sandbox policy for this turn and subsequent turns. */ sandboxPolicy?: SandboxPolicy | null, /** diff --git a/src/app-server/v2/index.ts b/src/app-server/v2/index.ts index 5f9ee1ff..a7295a3d 100644 --- a/src/app-server/v2/index.ts +++ b/src/app-server/v2/index.ts @@ -21,6 +21,7 @@ export type { AppSummary } from "./AppSummary"; export type { AppToolApproval } from "./AppToolApproval"; export type { AppToolsConfig } from "./AppToolsConfig"; export type { ApprovalDecision } from "./ApprovalDecision"; +export type { ApprovalsReviewer } from "./ApprovalsReviewer"; export type { AppsConfig } from "./AppsConfig"; export type { AppsDefaultConfig } from "./AppsDefaultConfig"; export type { AppsListParams } from "./AppsListParams"; @@ -100,12 +101,30 @@ export type { FileChangeOutputDeltaNotification } from "./FileChangeOutputDeltaN export type { FileChangeRequestApprovalParams } from "./FileChangeRequestApprovalParams"; export type { FileChangeRequestApprovalResponse } from "./FileChangeRequestApprovalResponse"; export type { FileUpdateChange } from "./FileUpdateChange"; +export type { FsCopyParams } from "./FsCopyParams"; +export type { FsCopyResponse } from "./FsCopyResponse"; +export type { FsCreateDirectoryParams } from "./FsCreateDirectoryParams"; +export type { FsCreateDirectoryResponse } from "./FsCreateDirectoryResponse"; +export type { FsGetMetadataParams } from "./FsGetMetadataParams"; +export type { FsGetMetadataResponse } from "./FsGetMetadataResponse"; +export type { FsReadDirectoryEntry } from "./FsReadDirectoryEntry"; +export type { FsReadDirectoryParams } from "./FsReadDirectoryParams"; +export type { FsReadDirectoryResponse } from "./FsReadDirectoryResponse"; +export type { FsReadFileParams } from "./FsReadFileParams"; +export type { FsReadFileResponse } from "./FsReadFileResponse"; +export type { FsRemoveParams } from "./FsRemoveParams"; +export type { FsRemoveResponse } from "./FsRemoveResponse"; +export type { FsWriteFileParams } from "./FsWriteFileParams"; +export type { FsWriteFileResponse } from "./FsWriteFileResponse"; export type { GetAccountParams } from "./GetAccountParams"; export type { GetAccountRateLimitsResponse } from "./GetAccountRateLimitsResponse"; export type { GetAccountResponse } from "./GetAccountResponse"; export type { GitInfo } from "./GitInfo"; export type { GrantedMacOsPermissions } from "./GrantedMacOsPermissions"; export type { GrantedPermissionProfile } from "./GrantedPermissionProfile"; +export type { GuardianApprovalReview } from "./GuardianApprovalReview"; +export type { GuardianApprovalReviewStatus } from "./GuardianApprovalReviewStatus"; +export type { GuardianRiskLevel } from "./GuardianRiskLevel"; export type { HazelnutScope } from "./HazelnutScope"; export type { HookCompletedNotification } from "./HookCompletedNotification"; export type { HookEventName } from "./HookEventName"; @@ -118,6 +137,8 @@ export type { HookRunSummary } from "./HookRunSummary"; export type { HookScope } from "./HookScope"; export type { HookStartedNotification } from "./HookStartedNotification"; export type { ItemCompletedNotification } from "./ItemCompletedNotification"; +export type { ItemGuardianApprovalReviewCompletedNotification } from "./ItemGuardianApprovalReviewCompletedNotification"; +export type { ItemGuardianApprovalReviewStartedNotification } from "./ItemGuardianApprovalReviewStartedNotification"; export type { ItemStartedNotification } from "./ItemStartedNotification"; export type { ListMcpServerStatusParams } from "./ListMcpServerStatusParams"; export type { ListMcpServerStatusResponse } from "./ListMcpServerStatusResponse"; @@ -126,6 +147,7 @@ export type { ListMcpServersResponse } from "./ListMcpServersResponse"; export type { LoginAccountParams } from "./LoginAccountParams"; export type { LoginAccountResponse } from "./LoginAccountResponse"; export type { LogoutAccountResponse } from "./LogoutAccountResponse"; +export type { MarketplaceInterface } from "./MarketplaceInterface"; export type { McpAuthStatus } from "./McpAuthStatus"; export type { McpElicitationArrayType } from "./McpElicitationArrayType"; export type { McpElicitationBooleanSchema } from "./McpElicitationBooleanSchema"; @@ -162,6 +184,8 @@ export type { McpToolCallError } from "./McpToolCallError"; export type { McpToolCallProgressNotification } from "./McpToolCallProgressNotification"; export type { McpToolCallResult } from "./McpToolCallResult"; export type { McpToolCallStatus } from "./McpToolCallStatus"; +export type { MemoryCitation } from "./MemoryCitation"; +export type { MemoryCitationEntry } from "./MemoryCitationEntry"; export type { MergeStrategy } from "./MergeStrategy"; export type { Model } from "./Model"; export type { ModelAvailabilityNux } from "./ModelAvailabilityNux"; @@ -183,12 +207,17 @@ export type { PermissionGrantScope } from "./PermissionGrantScope"; export type { PermissionsRequestApprovalParams } from "./PermissionsRequestApprovalParams"; export type { PermissionsRequestApprovalResponse } from "./PermissionsRequestApprovalResponse"; export type { PlanDeltaNotification } from "./PlanDeltaNotification"; +export type { PluginAuthPolicy } from "./PluginAuthPolicy"; +export type { PluginDetail } from "./PluginDetail"; export type { PluginInstallParams } from "./PluginInstallParams"; +export type { PluginInstallPolicy } from "./PluginInstallPolicy"; export type { PluginInstallResponse } from "./PluginInstallResponse"; export type { PluginInterface } from "./PluginInterface"; export type { PluginListParams } from "./PluginListParams"; export type { PluginListResponse } from "./PluginListResponse"; export type { PluginMarketplaceEntry } from "./PluginMarketplaceEntry"; +export type { PluginReadParams } from "./PluginReadParams"; +export type { PluginReadResponse } from "./PluginReadResponse"; export type { PluginSource } from "./PluginSource"; export type { PluginSummary } from "./PluginSummary"; export type { PluginUninstallParams } from "./PluginUninstallParams"; @@ -204,6 +233,7 @@ export type { ReasoningSummaryPartAddedNotification } from "./ReasoningSummaryPa export type { ReasoningSummaryTextDeltaNotification } from "./ReasoningSummaryTextDeltaNotification"; export type { ReasoningTextDeltaNotification } from "./ReasoningTextDeltaNotification"; export type { RemoteSkillSummary } from "./RemoteSkillSummary"; +export type { RequestPermissionProfile } from "./RequestPermissionProfile"; export type { ResidencyRequirement } from "./ResidencyRequirement"; export type { ReviewDelivery } from "./ReviewDelivery"; export type { ReviewStartParams } from "./ReviewStartParams"; @@ -223,6 +253,7 @@ export type { SkillMetadata } from "./SkillMetadata"; export type { SkillRequestApprovalParams } from "./SkillRequestApprovalParams"; export type { SkillRequestApprovalResponse } from "./SkillRequestApprovalResponse"; export type { SkillScope } from "./SkillScope"; +export type { SkillSummary } from "./SkillSummary"; export type { SkillToolDependency } from "./SkillToolDependency"; export type { SkillsChangedNotification } from "./SkillsChangedNotification"; export type { SkillsConfigWriteParams } from "./SkillsConfigWriteParams"; diff --git a/src/login.ts b/src/login.ts index 7de446d3..c9c78188 100644 --- a/src/login.ts +++ b/src/login.ts @@ -89,6 +89,7 @@ async function login(options: LoginOptions): Promise { } logger.log("Starting ChatGPT login..."); + const loginCompletedVersion = appServerClient.getAccountLoginCompletedVersion(); const loginResponse = await appServerClient.accountLogin({type: "chatgpt"}); if (loginResponse.type === "chatgpt") { @@ -100,7 +101,7 @@ async function login(options: LoginOptions): Promise { } logger.log("Waiting for login completion..."); - const result = await appServerClient.awaitLoginCompleted(); + const result = await appServerClient.awaitLoginCompleted(loginCompletedVersion); if (result.success) { logger.log("Login successful!");