// server/modules/orchestration/a2a/adapters/cursor.adapter.ts
// Wraps the existing server/cursor-cli.js runtime, which emits
// provider-neutral NormalizedMessage frames over a writer interface.
import crypto from 'node:crypto';
// eslint-disable-next-line boundaries/no-unknown -- cursor-cli.js is a top-level CLI runtime not yet classified by eslint.config.js; cleanup deferred.
import { abortCursorSession, spawnCursor } from '../../../../cursor-cli.js';
import { AbstractA2AAdapter } from '../../../../modules/orchestration/a2a/adapters/abstract-a2a.adapter.js';
function joinPartsToPrompt(parts) {
    return parts
        .map((part) => {
        if (part.kind === 'text')
            return part.text;
        if (part.kind === 'data')
            return JSON.stringify(part.data);
        return `[file:${part.name}${part.uri ? ` uri=${part.uri}` : ''}]`;
    })
        .join('\n');
}
function newId(prefix) {
    return `${prefix}_${crypto.randomBytes(8).toString('hex')}`;
}
export class CursorA2AAdapter extends AbstractA2AAdapter {
    id = 'cursor';
    agentCard = {
        name: 'pixcode-cursor',
        description: 'Cursor CLI, accessed via Pixcode',
        url: '/hermes/agents/cursor',
        version: '1.0.0',
        capabilities: ['streaming', 'fileEdit', 'commandExec'],
        skills: [
            {
                id: 'rapid-prototyping',
                description: 'Fast implementation for coding tasks in Cursor CLI',
            },
            {
                id: 'reasoning-assist',
                description: 'Emit reasoning and tool traces during execution',
            },
            {
                id: 'multi-file-refactor',
                description: 'Coordinate edits across multiple files',
            },
        ],
        authentication: { type: 'bearer' },
    };
    active = new Map();
    async submitTask(task, ctx) {
        const promptText = joinPartsToPrompt(task.history[task.history.length - 1]?.parts ?? []);
        const session = { sessionId: null };
        this.active.set(task.id, session);
        this.emitState(task.id, 'working');
        const fakeWriter = {
            send: (data) => this.handleRuntimeFrame(task.id, data, session),
            getSessionId: () => session.sessionId,
            setSessionId: (sessionId) => {
                session.sessionId = sessionId;
            },
        };
        const finished = (async () => {
            try {
                await spawnCursor(promptText, {
                    cwd: ctx.cwd,
                    model: ctx.model,
                    toolsSettings: ctx.toolsSettings,
                    skipPermissions: ctx.permissionMode === 'acceptEdits' || ctx.permissionMode === 'bypassPermissions',
                }, fakeWriter);
                if (this.active.has(task.id)) {
                    this.emitState(task.id, 'completed');
                }
            }
            catch (err) {
                if (this.active.has(task.id)) {
                    this.emitState(task.id, 'failed', {
                        code: 'ADAPTER_RUNTIME_ERROR',
                        message: err instanceof Error ? err.message : String(err),
                    });
                }
            }
            finally {
                this.active.delete(task.id);
            }
        })();
        return {
            cancel: () => this.cancelTask(task.id),
            finished,
        };
    }
    async cancelTask(taskId) {
        const session = this.active.get(taskId);
        if (!session) {
            this.emitState(taskId, 'canceled');
            return;
        }
        this.active.delete(taskId);
        if (session.sessionId) {
            abortCursorSession(session.sessionId);
        }
        this.emitState(taskId, 'canceled');
    }
    handleRuntimeFrame(taskId, frame, session) {
        if (!frame || typeof frame !== 'object')
            return;
        const message = frame;
        if (message.kind === 'session_created' &&
            typeof message.newSessionId === 'string' &&
            !session.sessionId) {
            session.sessionId = message.newSessionId;
            return;
        }
        switch (message.kind) {
            case 'stream_delta':
            case 'text': {
                const text = typeof message.content === 'string' ? message.content : null;
                if (text && text.trim()) {
                    this.emitMessage(taskId, {
                        messageId: typeof message.id === 'string' ? message.id : newId('msg'),
                        role: message.role === 'user' ? 'user' : 'agent',
                        parts: [{ kind: 'text', text }],
                        taskId,
                    });
                }
                return;
            }
            case 'thinking': {
                this.emitArtifact(taskId, {
                    artifactId: typeof message.id === 'string' ? message.id : newId('art'),
                    type: 'data',
                    parts: [
                        {
                            kind: 'data',
                            data: {
                                kind: 'thinking',
                                content: message.content,
                            },
                        },
                    ],
                    metadata: { source: 'cursor-thinking' },
                });
                return;
            }
            case 'tool_use': {
                this.emitArtifact(taskId, {
                    artifactId: typeof message.id === 'string' ? message.id : newId('art'),
                    type: 'command-output',
                    parts: [
                        {
                            kind: 'data',
                            data: {
                                toolName: message.toolName,
                                toolInput: message.toolInput,
                                toolId: message.toolId,
                                toolResult: message.toolResult,
                            },
                        },
                    ],
                    metadata: { source: 'cursor-tool-use' },
                });
                return;
            }
            case 'tool_result': {
                this.emitArtifact(taskId, {
                    artifactId: typeof message.id === 'string' ? message.id : newId('art'),
                    type: 'command-output',
                    parts: [
                        {
                            kind: 'data',
                            data: {
                                toolId: message.toolId,
                                content: message.content,
                                isError: message.isError,
                            },
                        },
                    ],
                    metadata: { source: 'cursor-tool-result' },
                });
                return;
            }
            case 'status':
            case 'stream_end':
            case 'complete':
            case 'session_created':
                return;
            case 'error': {
                const text = typeof message.content === 'string' && message.content.length > 0
                    ? message.content
                    : 'Cursor reported an error';
                this.emitState(taskId, 'failed', {
                    code: 'ADAPTER_RUNTIME_ERROR',
                    message: text,
                });
                this.active.delete(taskId);
                return;
            }
            default: {
                this.emitArtifact(taskId, {
                    artifactId: typeof message.id === 'string' ? message.id : newId('art'),
                    type: 'data',
                    parts: [{ kind: 'data', data: message }],
                    metadata: { source: `cursor-${String(message.kind ?? 'event')}` },
                });
            }
        }
    }
}
//# sourceMappingURL=cursor.adapter.js.map