Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions apps/codex-claw/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Route as ApiPathsRouteImport } from './routes/api/paths'
import { Route as ApiMcpHealthRouteImport } from './routes/api/mcp-health'
import { Route as ApiHistoryRouteImport } from './routes/api/history'
import { Route as ApiGitReviewRouteImport } from './routes/api/git-review'
import { Route as ApiContextPreviewRouteImport } from './routes/api/context-preview'
import { Route as ApiArtifactsRouteImport } from './routes/api/artifacts'

const NewRoute = NewRouteImport.update({
Expand Down Expand Up @@ -101,6 +102,11 @@ const ApiGitReviewRoute = ApiGitReviewRouteImport.update({
path: '/api/git-review',
getParentRoute: () => rootRouteImport,
} as any)
const ApiContextPreviewRoute = ApiContextPreviewRouteImport.update({
id: '/api/context-preview',
path: '/api/context-preview',
getParentRoute: () => rootRouteImport,
} as any)
const ApiArtifactsRoute = ApiArtifactsRouteImport.update({
id: '/api/artifacts',
path: '/api/artifacts',
Expand All @@ -112,6 +118,7 @@ export interface FileRoutesByFullPath {
'/connect': typeof ConnectRoute
'/new': typeof NewRoute
'/api/artifacts': typeof ApiArtifactsRoute
'/api/context-preview': typeof ApiContextPreviewRoute
'/api/git-review': typeof ApiGitReviewRoute
'/api/history': typeof ApiHistoryRoute
'/api/mcp-health': typeof ApiMcpHealthRoute
Expand All @@ -130,6 +137,7 @@ export interface FileRoutesByTo {
'/connect': typeof ConnectRoute
'/new': typeof NewRoute
'/api/artifacts': typeof ApiArtifactsRoute
'/api/context-preview': typeof ApiContextPreviewRoute
'/api/git-review': typeof ApiGitReviewRoute
'/api/history': typeof ApiHistoryRoute
'/api/mcp-health': typeof ApiMcpHealthRoute
Expand All @@ -149,6 +157,7 @@ export interface FileRoutesById {
'/connect': typeof ConnectRoute
'/new': typeof NewRoute
'/api/artifacts': typeof ApiArtifactsRoute
'/api/context-preview': typeof ApiContextPreviewRoute
'/api/git-review': typeof ApiGitReviewRoute
'/api/history': typeof ApiHistoryRoute
'/api/mcp-health': typeof ApiMcpHealthRoute
Expand All @@ -169,6 +178,7 @@ export interface FileRouteTypes {
| '/connect'
| '/new'
| '/api/artifacts'
| '/api/context-preview'
| '/api/git-review'
| '/api/history'
| '/api/mcp-health'
Expand All @@ -187,6 +197,7 @@ export interface FileRouteTypes {
| '/connect'
| '/new'
| '/api/artifacts'
| '/api/context-preview'
| '/api/git-review'
| '/api/history'
| '/api/mcp-health'
Expand All @@ -205,6 +216,7 @@ export interface FileRouteTypes {
| '/connect'
| '/new'
| '/api/artifacts'
| '/api/context-preview'
| '/api/git-review'
| '/api/history'
| '/api/mcp-health'
Expand All @@ -224,6 +236,7 @@ export interface RootRouteChildren {
ConnectRoute: typeof ConnectRoute
NewRoute: typeof NewRoute
ApiArtifactsRoute: typeof ApiArtifactsRoute
ApiContextPreviewRoute: typeof ApiContextPreviewRoute
ApiGitReviewRoute: typeof ApiGitReviewRoute
ApiHistoryRoute: typeof ApiHistoryRoute
ApiMcpHealthRoute: typeof ApiMcpHealthRoute
Expand Down Expand Up @@ -345,6 +358,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ApiGitReviewRouteImport
parentRoute: typeof rootRouteImport
}
'/api/context-preview': {
id: '/api/context-preview'
path: '/api/context-preview'
fullPath: '/api/context-preview'
preLoaderRoute: typeof ApiContextPreviewRouteImport
parentRoute: typeof rootRouteImport
}
'/api/artifacts': {
id: '/api/artifacts'
path: '/api/artifacts'
Expand All @@ -360,6 +380,7 @@ const rootRouteChildren: RootRouteChildren = {
ConnectRoute: ConnectRoute,
NewRoute: NewRoute,
ApiArtifactsRoute: ApiArtifactsRoute,
ApiContextPreviewRoute: ApiContextPreviewRoute,
ApiGitReviewRoute: ApiGitReviewRoute,
ApiHistoryRoute: ApiHistoryRoute,
ApiMcpHealthRoute: ApiMcpHealthRoute,
Expand Down
23 changes: 23 additions & 0 deletions apps/codex-claw/src/routes/api/context-preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createFileRoute } from '@tanstack/react-router'
import { json } from '@tanstack/react-start'
import { previewContextAttachment } from '../../server/context-attachments'

export const Route = createFileRoute('/api/context-preview')({
server: {
handlers: {
POST: async ({ request }) => {
try {
const body = await request.json().catch(() => ({}))
return json(await previewContextAttachment(body))
} catch (err) {
return json(
{
error: err instanceof Error ? err.message : String(err),
},
{ status: 400 },
)
}
},
},
},
})
21 changes: 20 additions & 1 deletion apps/codex-claw/src/routes/api/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import {
resolveCodexSession,
sendCodexPrompt,
} from '../../server/codex-cli'
import {
buildContextAttachmentPrompt,
parseContextAttachments,
} from '../../server/context-attachments'
import { buildRepositoryContextPrompt } from '../../server/repo-context'
import type { RepoContextSelection } from '../../server/repo-context'

Expand Down Expand Up @@ -141,10 +145,25 @@ export const Route = createFileRoute('/api/send')({
const contextSelections = parseContextSelections(
body.contextSelections,
)
const contextBlock =
const parsedContextAttachments = parseContextAttachments(
body.contextAttachments,
)
if (!parsedContextAttachments.ok) {
return json(
{ ok: false, error: parsedContextAttachments.error },
{ status: 400 },
)
}
const repositoryContextBlock =
contextSelections.length > 0
? buildRepositoryContextPrompt(contextSelections)
: undefined
const attachmentContextBlock = buildContextAttachmentPrompt(
parsedContextAttachments.attachments,
)
const contextBlock = [repositoryContextBlock, attachmentContextBlock]
.filter(Boolean)
.join('\n\n')

if (
!message.trim() &&
Expand Down
14 changes: 14 additions & 0 deletions apps/codex-claw/src/screens/chat/chat-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { getMessageTimestamp, normalizeSessions, readError } from './utils'
import type { QueryClient } from '@tanstack/react-query'
import type {
ArtifactListResponse,
ContextAttachment,
ContextAttachmentPreviewInput,
GatewayMessage,
GitReviewPayload,
HistoryResponse,
Expand Down Expand Up @@ -210,6 +212,18 @@ export async function fetchRepoContext(
return (await res.json()) as RepoContextPayload
}

export async function previewContextAttachment(
input: ContextAttachmentPreviewInput,
): Promise<ContextAttachment> {
const res = await fetch('/api/context-preview', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(input),
})
if (!res.ok) throw new Error(await readError(res))
return (await res.json()) as ContextAttachment
}

export async function fetchGitReview(): Promise<GitReviewPayload> {
const res = await fetch('/api/git-review')
if (!res.ok) throw new Error(await readError(res))
Expand Down
30 changes: 23 additions & 7 deletions apps/codex-claw/src/screens/chat/chat-screen-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { GatewayMessage, RepoContextSelection } from './types'
import type {
ContextAttachment,
GatewayMessage,
RepoContextSelection,
} from './types'
import type { AttachmentFile } from '@/components/attachment-button'
import { randomUUID } from '@/lib/utils'

Expand All @@ -12,6 +16,7 @@ export function createOptimisticMessage(
body: string,
attachments?: Array<AttachmentFile>,
contextSelections?: Array<RepoContextSelection>,
contextAttachments?: Array<ContextAttachment>,
): OptimisticMessagePayload {
const clientId = randomUUID()
const optimisticId = `opt-${clientId}`
Expand Down Expand Up @@ -40,13 +45,24 @@ export function createOptimisticMessage(

if (body.trim()) {
content.push({ type: 'text', text: body })
} else if (contextSelections && contextSelections.length > 0) {
content.push({
type: 'text',
text:
} else if (
(contextSelections && contextSelections.length > 0) ||
(contextAttachments && contextAttachments.length > 0)
) {
const labels: Array<string> = []
if (contextSelections && contextSelections.length > 0) {
labels.push(
'Repository context: ' +
contextSelections.map((selection) => selection.path).join(', '),
})
contextSelections.map((selection) => selection.path).join(', '),
)
}
if (contextAttachments && contextAttachments.length > 0) {
labels.push(
'Context attachments: ' +
contextAttachments.map((attachment) => attachment.title).join(', '),
)
}
content.push({ type: 'text', text: labels.join('\n') })
} else if (attachments && attachments.length > 0) {
content.push({ type: 'text', text: '' })
}
Expand Down
33 changes: 30 additions & 3 deletions apps/codex-claw/src/screens/chat/chat-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ import { shouldRedirectToConnect } from './hooks/use-chat-error-state'
import { useChatRedirect } from './hooks/use-chat-redirect'
import type { AttachmentFile } from '@/components/attachment-button'
import type { ChatComposerHelpers } from './components/chat-composer'
import type { RepoContextSelection, RunProfileId } from './types'
import type {
ContextAttachment,
RepoContextSelection,
RunProfileId,
} from './types'
import { useExport } from '@/hooks/use-export'
import { useChatSettings } from '@/hooks/use-chat-settings'
import { cn, randomUUID } from '@/lib/utils'
Expand Down Expand Up @@ -240,6 +244,7 @@ export function ChatScreen({
skipOptimistic = false,
attachments?: Array<AttachmentFile>,
contextSelections?: Array<RepoContextSelection>,
contextAttachments?: Array<ContextAttachment>,
runProfile?: RunProfileId,
confirmedRisk?: boolean,
) {
Expand All @@ -249,6 +254,7 @@ export function ChatScreen({
body,
attachments,
contextSelections,
contextAttachments,
)
optimisticClientId = clientId
appendHistoryMessage(
Expand All @@ -275,6 +281,17 @@ export function ChatScreen({
content: a.base64,
name: a.file.name,
}))
const contextAttachmentsPayload = contextAttachments?.map((attachment) => ({
id: attachment.id,
kind: attachment.kind,
title: attachment.title,
source: attachment.source,
mimeType: attachment.mimeType,
sizeBytes: attachment.sizeBytes,
estimatedTokens: attachment.estimatedTokens,
text: attachment.text,
truncated: attachment.truncated,
}))

fetch('/api/send', {
method: 'POST',
Expand All @@ -287,6 +304,7 @@ export function ChatScreen({
idempotencyKey: randomUUID(),
attachments: attachmentsPayload,
contextSelections,
contextAttachments: contextAttachmentsPayload,
runProfile,
confirmedRisk,
}),
Expand Down Expand Up @@ -367,19 +385,26 @@ export function ChatScreen({
(body: string, helpers: ChatComposerHelpers) => {
const attachments = helpers.attachments
const contextSelections = helpers.contextSelections
const contextAttachments = helpers.contextAttachments
const runProfile = helpers.runProfile
const confirmedRisk = helpers.confirmedRisk
if (
body.length === 0 &&
(!attachments || attachments.length === 0) &&
(!contextSelections || contextSelections.length === 0)
(!contextSelections || contextSelections.length === 0) &&
(!contextAttachments || contextAttachments.length === 0)
)
return
helpers.reset()

if (isNewChat) {
const { clientId, optimisticId, optimisticMessage } =
createOptimisticMessage(body, attachments, contextSelections)
createOptimisticMessage(
body,
attachments,
contextSelections,
contextAttachments,
)
appendHistoryMessage(queryClient, 'new', 'new', optimisticMessage)
setPendingGeneration(true)
setSending(true)
Expand All @@ -396,6 +421,7 @@ export function ChatScreen({
optimisticMessage,
attachments,
contextSelections,
contextAttachments,
runProfile,
confirmedRisk,
})
Expand Down Expand Up @@ -438,6 +464,7 @@ export function ChatScreen({
false,
attachments,
contextSelections,
contextAttachments,
runProfile,
confirmedRisk,
)
Expand Down
Loading
Loading