feat: add codex cloud delegation (#315)

This commit is contained in:
Brad Groux 2026-05-05 21:53:47 -05:00 committed by GitHub
parent 42fb759cca
commit 7bf8df6666
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 490 additions and 19 deletions

View file

@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [4.2.2] - 2026-05-06
### Added
- Codex Cloud delegation through GitHub issue, issue-comment, and PR-comment workflows.
- `POST /api/github/codex/delegate` for creating scoped `@codex` GitHub delegation prompts from Veritas tasks.
- Disabled-by-default `codex-cloud` agent profile and `codex-cloud` provider metadata support.
- Cloud delegation attempt metadata with `cloudUrl` and `cloudTarget` fields.
- Task comments that link successful Codex Cloud delegations back to the GitHub artifact.
- Frontend API hook for Codex Cloud delegation.
### Changed
- Updated workspace package versions and README badge from `4.2.1` to `4.2.2`.
- Documented Codex Cloud delegation as implemented while workflow steps, review actions, and richer Settings checks remain next in the v4.2 patch train.
## [4.2.1] - 2026-05-06
### Added

View file

@ -10,7 +10,7 @@ Built for developers who want a visual Kanban board that works with autonomous c
[![CI](https://github.com/BradGroux/veritas-kanban/actions/workflows/ci.yml/badge.svg)](https://github.com/BradGroux/veritas-kanban/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Version](https://img.shields.io/badge/version-4.2.1-blue.svg)](CHANGELOG.md)
[![Version](https://img.shields.io/badge/version-4.2.2-blue.svg)](CHANGELOG.md)
[![TypeScript](https://img.shields.io/badge/TypeScript-6.0-blue.svg)](https://www.typescriptlang.org/)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)

View file

@ -1,6 +1,6 @@
{
"name": "@veritas-kanban/cli",
"version": "4.2.1",
"version": "4.2.2",
"description": "CLI for Veritas Kanban task management",
"type": "module",
"bin": {

View file

@ -2,7 +2,7 @@
Release target: **Veritas Kanban v4.2**
This roadmap tracks the first-class OpenAI Codex integration work for Veritas Kanban. v4.2 now includes local `codex exec` execution and SDK-backed local Codex sessions. The remaining work expands that foundation into cloud delegation, workflow-engine execution, review automation, and deeper Settings health checks.
This roadmap tracks the first-class OpenAI Codex integration work for Veritas Kanban. v4.2 now includes local `codex exec` execution, SDK-backed local Codex sessions, and GitHub-native Codex Cloud delegation. The remaining work expands that foundation into workflow-engine execution, review automation, and deeper Settings health checks.
Companion docs:
@ -51,11 +51,25 @@ const thread = codex.startThread({
### Codex Cloud Delegation
Cloud delegation should start through GitHub-native workflows: Veritas can create or comment on GitHub issues/PRs with scoped `@codex` prompts, then sync links and outcomes back into the task. If official cloud APIs become available, they can be added behind the same provider boundary.
Cloud delegation starts through GitHub-native workflows: Veritas can create or comment on GitHub issues/PRs with scoped `@codex` prompts, then sync links and outcomes back into the task. If official cloud APIs become available, they can be added behind the same provider boundary.
API shape:
```http
POST /api/github/codex/delegate
```
```json
{
"taskId": "task_...",
"target": "issue",
"model": "gpt-5.5"
}
```
## Architecture Direction
v4.2 starts with a focused provider seam inside the existing agent service instead of a full rewrite. `codex` agents resolve to the local Codex CLI runner, `codex-sdk` agents resolve to the SDK session runner, and existing agents keep the OpenClaw request-file behavior. A fuller provider abstraction is still tracked for workflow and review execution.
v4.2 starts with a focused provider seam inside the existing agent service instead of a full rewrite. `codex` agents resolve to the local Codex CLI runner, `codex-sdk` agents resolve to the SDK session runner, `codex-cloud` uses GitHub-native delegation, and existing agents keep the OpenClaw request-file behavior. A fuller provider abstraction is still tracked for workflow and review execution.
Expected long-term provider capabilities:
@ -73,6 +87,7 @@ The provider abstraction should support:
- OpenClaw compatibility through an OpenClaw provider adapter.
- Codex CLI through a local process provider.
- Codex SDK through the implemented thread/session provider.
- Codex Cloud through GitHub issue/PR delegation.
- Future providers without route-level branching.
## Telemetry And Logs

View file

@ -189,6 +189,7 @@ Expected behavior:
- provider mode `codex-cloud`
- delegation timestamp
- sync status
- cloud attempt metadata (`cloudTarget`, `cloudUrl`)
5. Human reviews the Codex Cloud PR and links it back to the Veritas task.

View file

@ -295,18 +295,18 @@ First-class support for autonomous coding agents.
## OpenAI Codex Integration (v4.2)
v4.2 adds first-class OpenAI Codex support: local `codex exec` attempts plus SDK-backed Codex sessions that run in task worktrees and report status, logs, completion, telemetry, and durable thread IDs through the existing Veritas agent lifecycle.
v4.2 adds first-class OpenAI Codex support: local `codex exec` attempts, SDK-backed Codex sessions, and GitHub-native Codex Cloud delegation through the existing Veritas task lifecycle.
Implemented:
- **Codex CLI provider** — Runs `codex exec --json` in the task worktree, maps JSONL/stdout/stderr into Veritas attempt logs, records final summaries, and emits run/token telemetry when available.
- **Codex agent defaults** — Adds a disabled-by-default OpenAI Codex agent profile with `codex exec --sandbox workspace-write --json`.
- **Codex SDK provider** — Uses `@openai/codex-sdk` to start durable local Codex threads, stream SDK events into attempt logs, persist `threadId` on attempts, and emit token telemetry from completed turns.
- **Codex Cloud delegation** — Creates scoped `@codex` GitHub issue/PR prompts, records cloud attempt metadata, and links the GitHub artifact back to the Veritas task.
- **Config migration** — Existing configs receive the missing built-in Codex agent without overwriting customized agents.
Still planned:
- **Codex Cloud delegation** — Creates GitHub issue/PR delegation prompts for cloud Codex work and links GitHub artifacts back to Veritas tasks.
- **Codex review actions** — Uses Codex to review task branches, PR diffs, and failed changes.
- **Workflow Codex steps** — Executes workflow-engine agent steps through the same provider abstraction used by local task starts.
- **Richer Settings health checks** — Detects Codex install, auth, SDK availability, and profile readiness from Settings.

View file

@ -1,6 +1,6 @@
# SOP: OpenAI Codex Integration
Use this playbook when Veritas Kanban delegates work to OpenAI Codex. v4.2 includes local Codex CLI execution through `codex exec` and SDK-backed local Codex sessions; Cloud delegation, workflow-engine execution, review actions, and richer Settings checks continue through the v4.2 patch train.
Use this playbook when Veritas Kanban delegates work to OpenAI Codex. v4.2 includes local Codex CLI execution through `codex exec`, SDK-backed local Codex sessions, and GitHub-native Codex Cloud delegation; workflow-engine execution, review actions, and richer Settings checks continue through the v4.2 patch train.
---
@ -133,6 +133,14 @@ Veritas persists the Codex thread ID in attempt metadata:
Use cloud delegation when the desired output is a GitHub issue/PR workflow rather than direct local worktree execution.
Veritas endpoint:
```bash
curl -X POST http://localhost:3001/api/github/codex/delegate \
-H "Content-Type: application/json" \
-d '{"taskId":"task_123","target":"issue"}'
```
Recommended prompt pattern:
```text
@ -153,7 +161,19 @@ Veritas context:
Please open a PR and include a concise implementation summary, tests run, and any follow-up risks.
```
Veritas should link the GitHub artifact back to the task and track cloud delegation as a provider attempt, even if execution happens outside the local runtime.
Veritas links the GitHub artifact back to the task and tracks cloud delegation as a provider attempt, even though execution happens outside the local runtime.
Attempt metadata:
```json
{
"agent": "codex-cloud",
"provider": "codex-cloud",
"status": "pending",
"cloudTarget": "issue",
"cloudUrl": "https://github.com/owner/repo/issues/123"
}
```
---

View file

@ -1,6 +1,6 @@
{
"name": "@veritas-kanban/mcp",
"version": "4.2.1",
"version": "4.2.2",
"description": "MCP server for Veritas Kanban",
"type": "module",
"main": "./dist/index.js",

View file

@ -1,6 +1,6 @@
{
"name": "veritas-kanban",
"version": "4.2.1",
"version": "4.2.2",
"private": true,
"description": "Local-first task management and AI agent orchestration platform",
"author": "Brad Groux <brad@digitalmeld.io>",

View file

@ -1,6 +1,6 @@
{
"name": "@veritas-kanban/server",
"version": "4.2.1",
"version": "4.2.2",
"private": true,
"type": "module",
"main": "./dist/index.js",

View file

@ -57,6 +57,13 @@ describe('ConfigService', () => {
provider: 'codex-sdk',
enabled: false,
}),
expect.objectContaining({
type: 'codex-cloud',
name: 'OpenAI Codex Cloud',
command: 'gh',
provider: 'codex-cloud',
enabled: false,
}),
])
);
});
@ -116,6 +123,11 @@ describe('ConfigService', () => {
command: 'codex',
provider: 'codex-sdk',
}),
expect.objectContaining({
type: 'codex-cloud',
command: 'gh',
provider: 'codex-cloud',
}),
])
);
});

View file

@ -202,6 +202,15 @@ describe('Config Routes (actual module)', () => {
provider: 'codex-sdk',
model: 'gpt-5.5',
},
{
type: 'codex-cloud',
name: 'OpenAI Codex Cloud',
command: 'gh',
args: [],
enabled: true,
provider: 'codex-cloud',
model: 'gpt-5.5',
},
];
mockConfigService.updateAgents.mockResolvedValue({ agents });
const res = await request(app).put('/api/config/agents').send(agents);

View file

@ -0,0 +1,78 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import express from 'express';
import request from 'supertest';
import { errorHandler } from '../../middleware/error-handler.js';
const { mockGithubService } = vi.hoisted(() => ({
mockGithubService: {
checkGhCli: vi.fn(),
createPR: vi.fn(),
openPRInBrowser: vi.fn(),
delegateToCodexCloud: vi.fn(),
},
}));
vi.mock('../../services/github-service.js', () => ({
GitHubService: function () {
return mockGithubService;
},
}));
vi.mock('../../services/github-sync-service.js', () => ({
getGitHubSyncService: () => ({
sync: vi.fn(),
getSyncState: vi.fn(),
getConfig: vi.fn(),
updateConfig: vi.fn(),
}),
}));
import githubRoutes from '../../routes/github.js';
describe('GitHub Codex Cloud routes', () => {
let app: express.Express;
beforeEach(() => {
vi.clearAllMocks();
app = express();
app.use(express.json());
app.use('/api/github', githubRoutes);
app.use(errorHandler);
});
it('delegates a task to Codex Cloud through GitHub', async () => {
mockGithubService.delegateToCodexCloud.mockResolvedValue({
taskId: 'task_123',
attemptId: 'attempt_cloud',
target: 'issue',
url: 'https://github.com/owner/repo/issues/42',
number: 42,
repo: 'owner/repo',
prompt: '@codex Please work on this task.',
});
const response = await request(app).post('/api/github/codex/delegate').send({
taskId: 'task_123',
target: 'issue',
model: 'gpt-5.5',
});
expect(response.status).toBe(201);
expect(response.body.url).toBe('https://github.com/owner/repo/issues/42');
expect(mockGithubService.delegateToCodexCloud).toHaveBeenCalledWith({
taskId: 'task_123',
target: 'issue',
model: 'gpt-5.5',
});
});
it('rejects invalid Codex Cloud targets', async () => {
const response = await request(app).post('/api/github/codex/delegate').send({
taskId: 'task_123',
target: 'discussion',
});
expect(response.status).toBe(400);
expect(mockGithubService.delegateToCodexCloud).not.toHaveBeenCalled();
});
});

View file

@ -26,7 +26,7 @@ const agentSchema = z.object({
command: z.string().min(1),
args: z.array(z.string()),
enabled: z.boolean(),
provider: z.enum(['openclaw', 'codex-cli', 'codex-sdk', 'custom']).optional(),
provider: z.enum(['openclaw', 'codex-cli', 'codex-sdk', 'codex-cloud', 'custom']).optional(),
model: z.string().optional(),
});

View file

@ -18,6 +18,14 @@ const createPRSchema = z.object({
draft: z.boolean().optional(),
});
const codexCloudDelegateSchema = z.object({
taskId: z.string().min(1),
target: z.enum(['issue', 'issue-comment', 'pr-comment']).optional(),
title: z.string().optional(),
prompt: z.string().optional(),
model: z.string().optional(),
});
const syncConfigSchema = z.object({
enabled: z.boolean().optional(),
repo: z.string().min(1).optional(),
@ -64,6 +72,25 @@ router.post(
})
);
// POST /api/github/codex/delegate - Delegate a task to Codex Cloud through GitHub
router.post(
'/codex/delegate',
asyncHandler(async (req, res) => {
let input;
try {
input = codexCloudDelegateSchema.parse(req.body);
} catch (error) {
if (error instanceof z.ZodError) {
throw new ValidationError('Validation failed', error.issues);
}
throw error;
}
const delegation = await githubService.delegateToCodexCloud(input);
res.status(201).json(delegation);
})
);
// ── GitHub Issues Sync routes ───────────────────────────────
// POST /api/github/sync - Trigger a manual sync

View file

@ -70,6 +70,14 @@ const DEFAULT_CONFIG: AppConfig = {
enabled: false,
provider: 'codex-sdk',
},
{
type: 'codex-cloud',
name: 'OpenAI Codex Cloud',
command: 'gh',
args: [],
enabled: false,
provider: 'codex-cloud',
},
],
defaultAgent: 'claude-code',
};

View file

@ -1,8 +1,10 @@
import { exec, execFile } from 'child_process';
import { promisify } from 'util';
import { nanoid } from 'nanoid';
import { ConfigService } from './config-service.js';
import { TaskService } from './task-service.js';
import { getBreaker } from './circuit-registry.js';
import type { AgentType, Task } from '@veritas-kanban/shared';
import { createLogger } from '../lib/logger.js';
const log = createLogger('github-service');
@ -27,6 +29,26 @@ export interface PRInfo {
baseBranch: string;
}
export type CodexCloudTarget = 'issue' | 'issue-comment' | 'pr-comment';
export interface CodexCloudDelegationInput {
taskId: string;
target?: CodexCloudTarget;
title?: string;
prompt?: string;
model?: string;
}
export interface CodexCloudDelegationResult {
taskId: string;
attemptId: string;
target: CodexCloudTarget;
url: string;
number?: number;
repo: string;
prompt: string;
}
export class GitHubService {
private configService: ConfigService;
private taskService: TaskService;
@ -115,6 +137,115 @@ export class GitHubService {
}
}
async delegateToCodexCloud(
input: CodexCloudDelegationInput
): Promise<CodexCloudDelegationResult> {
const task = await this.taskService.getTask(input.taskId);
if (!task) {
throw new Error('Task not found');
}
if (!task.git?.repo) {
throw new Error('Task must have a repository configured for Codex Cloud delegation');
}
const config = await this.configService.getConfig();
const repoConfig = config.repos.find((r) => r.name === task.git!.repo);
if (!repoConfig) {
throw new Error(`Repository "${task.git.repo}" not found in config`);
}
const ghStatus = await this.checkGhCli();
if (!ghStatus.installed) {
throw new Error('GitHub CLI (gh) is not installed. Install it with: brew install gh');
}
if (!ghStatus.authenticated) {
throw new Error('GitHub CLI is not authenticated. Run: gh auth login');
}
const repoPath = this.expandPath(repoConfig.path);
const repo = await this.getRepoNameWithOwner(repoPath);
const target = input.target || (task.git.prNumber || task.git.prUrl ? 'pr-comment' : 'issue');
const prompt = input.prompt || this.buildCodexCloudPrompt(task, repo);
const title = input.title || `Codex: ${task.title}`;
const ghBreaker = getBreaker('github');
let url = '';
let number: number | undefined;
if (target === 'issue') {
const { stdout } = await ghBreaker.execute(() =>
execFileAsync('gh', ['issue', 'create', '--title', title, '--body', prompt], {
cwd: repoPath,
})
);
url = stdout.trim();
number = this.extractIssueNumber(url);
if (number) {
await this.taskService.updateTask(input.taskId, {
github: { issueNumber: number, repo, url },
});
}
} else if (target === 'issue-comment') {
if (!task.github?.issueNumber) {
throw new Error('Task must be linked to a GitHub issue for issue-comment delegation');
}
const issueRef = String(task.github.issueNumber);
const { stdout } = await ghBreaker.execute(() =>
execFileAsync('gh', ['issue', 'comment', issueRef, '--body', prompt], { cwd: repoPath })
);
const issue = await this.getIssueInfo(repoPath, issueRef);
url = stdout.trim() || issue.url;
number = issue.number;
} else {
const prRef = task.git.prNumber ? String(task.git.prNumber) : task.git.prUrl;
if (!prRef) {
throw new Error('Task must have a GitHub PR for pr-comment delegation');
}
const { stdout } = await ghBreaker.execute(() =>
execFileAsync('gh', ['pr', 'comment', prRef, '--body', prompt], { cwd: repoPath })
);
const pr = await this.getPRInfo(repoPath, prRef);
url = stdout.trim() || pr.url;
number = pr.number;
}
const attemptId = `attempt_${nanoid(8)}`;
const now = new Date().toISOString();
const cloudComment = {
id: `comment_${nanoid(8)}`,
author: 'veritas',
text: `Delegated to Codex Cloud via GitHub ${target}: ${url}`,
timestamp: now,
};
await this.taskService.updateTask(input.taskId, {
status: 'in-progress',
agent: 'codex-cloud',
attempt: {
id: attemptId,
agent: 'codex-cloud' as AgentType,
status: 'pending',
started: now,
provider: 'codex-cloud',
model: input.model,
cloudUrl: url,
cloudTarget: target,
},
comments: [...(task.comments || []), cloudComment],
});
return {
taskId: input.taskId,
attemptId,
target,
url,
number,
repo,
prompt,
};
}
/**
* Create a GitHub PR for a task
*/
@ -264,6 +395,87 @@ export class GitHubService {
return lines.join('\n');
}
private buildCodexCloudPrompt(task: Task, repo: string): string {
const lines = [
'@codex Please work on this Veritas Kanban task.',
'',
`Task: ${task.id} - ${task.title}`,
`Repository: ${repo}`,
`Type: ${task.type}`,
`Priority: ${task.priority}`,
];
if (task.project) lines.push(`Project: ${task.project}`);
if (task.sprint) lines.push(`Sprint: ${task.sprint}`);
if (task.git?.baseBranch) lines.push(`Base branch: ${task.git.baseBranch}`);
if (task.git?.branch) lines.push(`Working branch: ${task.git.branch}`);
lines.push('', 'Task description:', task.description || 'No description provided.', '');
lines.push('Acceptance criteria:');
if (task.verificationSteps?.length) {
for (const step of task.verificationSteps) lines.push(`- ${step.description}`);
} else {
lines.push('- Implement the task as described.');
lines.push('- Run relevant checks and summarize the result.');
lines.push('- Open or update a GitHub PR with the completed work.');
}
lines.push('', 'When finished:');
lines.push('- Link the PR or final artifact back to this GitHub thread.');
lines.push('- Summarize changed files, validation performed, and any follow-up risks.');
lines.push('- Keep Veritas Kanban as the source of truth for task status.');
return lines.join('\n');
}
private async getRepoNameWithOwner(repoPath: string): Promise<string> {
try {
const { stdout } = await execFileAsync('gh', ['repo', 'view', '--json', 'nameWithOwner'], {
cwd: repoPath,
});
const parsed = JSON.parse(stdout) as { nameWithOwner?: string };
if (parsed.nameWithOwner) return parsed.nameWithOwner;
} catch {
// Fall back to the origin remote when gh cannot resolve repo metadata.
}
const remote = await this.getGitHubRemote(repoPath);
const match = remote?.match(/github\.com[:/](.+?\/.+?)(?:\.git)?$/);
if (match?.[1]) return match[1];
throw new Error('Could not determine GitHub owner/repo for Codex Cloud delegation');
}
private async getIssueInfo(
repoPath: string,
issueRef: string
): Promise<{ url: string; number?: number }> {
const { stdout } = await execFileAsync(
'gh',
['issue', 'view', issueRef, '--json', 'url,number'],
{
cwd: repoPath,
}
);
const parsed = JSON.parse(stdout) as { url: string; number?: number };
return parsed;
}
private async getPRInfo(
repoPath: string,
prRef: string
): Promise<{ url: string; number?: number }> {
const { stdout } = await execFileAsync('gh', ['pr', 'view', prRef, '--json', 'url,number'], {
cwd: repoPath,
});
const parsed = JSON.parse(stdout) as { url: string; number?: number };
return parsed;
}
private extractIssueNumber(url: string): number | undefined {
const match = url.match(/\/issues\/(\d+)/);
return match ? Number(match[1]) : undefined;
}
/**
* Open PR in browser
*/

View file

@ -1,6 +1,6 @@
{
"name": "@veritas-kanban/shared",
"version": "4.2.1",
"version": "4.2.2",
"private": true,
"type": "module",
"main": "./dist/index.js",

View file

@ -22,7 +22,7 @@ export interface AgentConfig {
command: string;
args: string[];
enabled: boolean;
provider?: 'openclaw' | 'codex-cli' | 'codex-sdk' | 'custom';
provider?: 'openclaw' | 'codex-cli' | 'codex-sdk' | 'codex-cloud' | 'custom';
model?: string;
}

View file

@ -15,7 +15,14 @@ export interface QaGateState {
passedBy?: string; // Who approved (e.g. 'human', agent slug, user name)
}
/** Built-in agent types. Custom agents use any string slug. */
export type BuiltInAgentType = 'claude-code' | 'amp' | 'copilot' | 'gemini' | 'codex' | 'veritas';
export type BuiltInAgentType =
| 'claude-code'
| 'amp'
| 'copilot'
| 'gemini'
| 'codex'
| 'codex-cloud'
| 'veritas';
export type AgentType = BuiltInAgentType | (string & {});
export type AttemptStatus = 'pending' | 'running' | 'complete' | 'failed';
export type BlockedCategory = 'waiting-on-feedback' | 'technical-snag' | 'prerequisite' | 'other';
@ -43,6 +50,8 @@ export interface TaskAttempt {
provider?: string;
model?: string;
threadId?: string;
cloudUrl?: string;
cloudTarget?: string;
}
export interface Subtask {
@ -165,6 +174,7 @@ export const ALLOWED_MIME_TYPES = [
export interface TaskGitHub {
issueNumber: number;
repo: string;
url?: string;
}
export interface Task {

View file

@ -1,6 +1,6 @@
{
"name": "@veritas-kanban/web",
"version": "4.2.1",
"version": "4.2.2",
"private": true,
"type": "module",
"scripts": {

View file

@ -1,7 +1,20 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { api, type GitHubStatus, type PRInfo, type CreatePRInput } from '../lib/api';
import {
api,
type GitHubStatus,
type PRInfo,
type CreatePRInput,
type CodexCloudDelegationInput,
type CodexCloudDelegationResult,
} from '../lib/api';
export type { GitHubStatus, PRInfo, CreatePRInput };
export type {
GitHubStatus,
PRInfo,
CreatePRInput,
CodexCloudDelegationInput,
CodexCloudDelegationResult,
};
/**
* Check GitHub CLI status
@ -38,3 +51,18 @@ export function useOpenPR() {
mutationFn: (taskId) => api.github.openPR(taskId),
});
}
/**
* Delegate a task to Codex Cloud via GitHub issue/PR workflows.
*/
export function useDelegateCodexCloud() {
const queryClient = useQueryClient();
return useMutation<CodexCloudDelegationResult, Error, CodexCloudDelegationInput>({
mutationFn: (input) => api.github.delegateCodexCloud(input),
onSuccess: (_data, variables) => {
queryClient.invalidateQueries({ queryKey: ['tasks'] });
queryClient.invalidateQueries({ queryKey: ['task', variables.taskId] });
},
});
}

View file

@ -91,6 +91,18 @@ export const githubApi = {
return handleResponse<PRInfo>(response);
},
delegateCodexCloud: async (
input: CodexCloudDelegationInput
): Promise<CodexCloudDelegationResult> => {
const response = await fetch(`${API_BASE}/github/codex/delegate`, {
credentials: 'include',
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(input),
});
return handleResponse<CodexCloudDelegationResult>(response);
},
openPR: async (taskId: string): Promise<void> => {
const response = await fetch(`${API_BASE}/github/pr/${taskId}/open`, {
credentials: 'include',
@ -194,3 +206,23 @@ export interface CreatePRInput {
targetBranch?: string;
draft?: boolean;
}
export type CodexCloudTarget = 'issue' | 'issue-comment' | 'pr-comment';
export interface CodexCloudDelegationInput {
taskId: string;
target?: CodexCloudTarget;
title?: string;
prompt?: string;
model?: string;
}
export interface CodexCloudDelegationResult {
taskId: string;
attemptId: string;
target: CodexCloudTarget;
url: string;
number?: number;
repo: string;
prompt: string;
}

View file

@ -80,6 +80,9 @@ export type {
GitHubStatus,
PRInfo,
CreatePRInput,
CodexCloudTarget,
CodexCloudDelegationInput,
CodexCloudDelegationResult,
} from './diff';
export type {