wip: first run of implementation

This commit is contained in:
2026-02-25 20:29:01 +01:00
parent 2e203fa3a9
commit 20ea499a6f
40 changed files with 2170 additions and 22 deletions

View File

@@ -16,6 +16,13 @@ import { ChatEngine } from './ChatEngine';
import { PostEngine } from './PostEngine';
import { MediaEngine } from './MediaEngine';
import { getPostMediaEngine } from './PostMediaEngine';
import { ProtocolResponseBuilder } from '../agentic/protocol/responseBuilder';
import { CapabilityRegistryService } from '../agentic/capabilities/registry';
import { validateProtocolRequestEnvelope } from '../agentic/protocol/validator';
import type { ProtocolResponseEnvelope } from '../agentic/protocol/types';
import { AgentTurnStateMachine, type AgentTurnState } from '../agentic/workflow/turnStateMachine';
import { WorkflowCheckpointStore } from '../agentic/workflow/checkpointStore';
import { getProtocolTelemetryService } from '../agentic/observability/protocolTelemetry';
// OpenCode Zen API endpoints
const ZEN_ANTHROPIC_URL = 'https://opencode.ai/zen/v1/messages';
@@ -77,6 +84,10 @@ export interface SendMessageOptions {
export interface SendMessageResult {
success: boolean;
message?: string;
envelope?: ProtocolResponseEnvelope;
protocolVersion?: '2.0';
traceId?: string;
warnings?: string[];
error?: string;
toolCalls?: Array<{ name: string; args: unknown }>;
}
@@ -131,6 +142,10 @@ export class OpenCodeManager {
private postEngine: PostEngine;
private mediaEngine: MediaEngine;
private getMainWindow: () => BrowserWindow | null;
private protocolResponseBuilder: ProtocolResponseBuilder;
private capabilityRegistry: CapabilityRegistryService;
private turnStateMachine: AgentTurnStateMachine;
private workflowCheckpointStore: WorkflowCheckpointStore;
private apiKey: string = '';
private abortControllers: Map<string, AbortController> = new Map();
@@ -144,6 +159,13 @@ export class OpenCodeManager {
this.postEngine = postEngine;
this.mediaEngine = mediaEngine;
this.getMainWindow = getMainWindow;
this.protocolResponseBuilder = new ProtocolResponseBuilder();
this.capabilityRegistry = new CapabilityRegistryService();
this.turnStateMachine = new AgentTurnStateMachine();
this.workflowCheckpointStore = new WorkflowCheckpointStore({
getSetting: async (key: string) => this.chatEngine.getSetting(key),
setSetting: async (key: string, value: string) => this.chatEngine.setSetting(key, value),
});
}
/**
@@ -275,10 +297,37 @@ export class OpenCodeManager {
// Build message history from DB (excluding system messages)
const dbMessages = conversation.messages.filter(m => m.role !== 'system');
const surface = metadata?.surface || 'tab';
const capabilities = this.capabilityRegistry.getSnapshot({ surface });
const requestEnvelope = {
protocolVersion: '2.0' as const,
surface,
messages: dbMessages
.filter((message) => message.role === 'user' || message.role === 'assistant' || message.role === 'system' || message.role === 'tool')
.map((message) => ({
role: message.role,
content: message.content || '',
})),
context: {
conversationId,
modelId,
},
capabilities,
};
const requestValidation = validateProtocolRequestEnvelope(requestEnvelope);
if (!requestValidation.ok) {
return {
success: false,
error: requestValidation.error?.message || 'Invalid protocol request envelope',
};
}
const surfaceHint = metadata?.surface
? `\n\n[Client UI surface: ${metadata.surface}. Render response UI for this surface while keeping content functionally equivalent.]`
: '';
const userMessageForModel = `${userMessage}${surfaceHint}`;
const capabilityHint = `\n\n[Protocol request envelope]\n${JSON.stringify(requestEnvelope, null, 2)}`;
const userMessageForModel = `${userMessage}${surfaceHint}${capabilityHint}`;
// Add the new user message
dbMessages.push({
conversationId,
@@ -336,6 +385,38 @@ export class OpenCodeManager {
});
}
const protocolResult = this.protocolResponseBuilder.build({
rawAssistantOutput: fullResponse,
surface,
capabilities,
});
const previousCheckpoint = await this.workflowCheckpointStore.load(conversationId);
const previousState: AgentTurnState = previousCheckpoint?.state || 'planning';
const nextState = this.turnStateMachine.transition({
previousState,
envelope: {
intent: protocolResult.envelope.intent,
needsInput: protocolResult.envelope.needsInput,
},
});
await this.workflowCheckpointStore.save({
conversationId,
state: nextState,
pendingFields: protocolResult.envelope.needsInput.fields.map((field) => field.key),
lastTraceId: protocolResult.envelope.traceId,
updatedAt: new Date().toISOString(),
});
const blockedActionWarnings = protocolResult.warnings.filter((warning) => warning.includes('Blocked unsupported action'));
getProtocolTelemetryService().recordTurn({
validEnvelope: !protocolResult.validationError,
repairAttempted: protocolResult.repairAttempted,
fallbackUsed: Boolean(protocolResult.validationError),
blockedActions: blockedActionWarnings.length,
});
// Generate title after first exchange
const userMsgCount = conversation.messages.filter(m => m.role === 'user').length;
if (userMsgCount === 0 && fullResponse) {
@@ -346,7 +427,11 @@ export class OpenCodeManager {
return {
success: true,
message: fullResponse,
message: protocolResult.envelope.assistantText,
envelope: protocolResult.envelope,
protocolVersion: protocolResult.envelope.protocolVersion,
traceId: protocolResult.traceId,
warnings: protocolResult.warnings,
toolCalls: toolCallsCollected.length > 0 ? toolCallsCollected : undefined,
};
} catch (error) {