diff --git a/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/SessionOrchestrator.kt b/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/SessionOrchestrator.kt index e3217747..6ae6e7ca 100644 --- a/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/SessionOrchestrator.kt +++ b/core/kernel/src/main/kotlin/com/correx/core/kernel/orchestration/SessionOrchestrator.kt @@ -10,6 +10,7 @@ import com.correx.core.context.model.ContextLayer import com.correx.core.context.model.ContextPack import com.correx.core.context.model.TokenBudget import com.correx.core.events.types.ContextEntryId +import com.correx.core.events.events.ArtifactCreatedEvent import com.correx.core.events.events.ApprovalDecisionResolvedEvent import com.correx.core.events.events.ApprovalRequestedEvent import com.correx.core.events.events.EventMetadata @@ -262,7 +263,10 @@ abstract class SessionOrchestrator( if (isCancelled(sessionId)) return StageExecutionResult.Failure("CANCELLED", retryable = false) val validationCtx = ValidationContext(graph = graph, sessionState = session.state) when (val outcome = mapValidationOutcome(sessionId, stageId, validationCtx)) { - is StageExecutionResult.Success -> verifyProduces(sessionId, stageId, stageConfig) + is StageExecutionResult.Success -> { + emitToolArtifacts(sessionId, stageId, stageConfig) + verifyProduces(sessionId, stageId, stageConfig) + } is StageExecutionResult.Failure -> outcome } } @@ -280,7 +284,9 @@ abstract class SessionOrchestrator( val parameters = runCatching { val element = Json.parseToJsonElement(toolCall.function.arguments) val jsonObject = element as? kotlinx.serialization.json.JsonObject ?: return@runCatching emptyMap() - jsonObject.entries.associate { (k, v) -> k to v.toString().removeSurrounding("\"") as Any } + jsonObject.entries.associate { (k, v) -> + k to ((v as? kotlinx.serialization.json.JsonPrimitive)?.content ?: v.toString()) as Any + } }.getOrElse { emptyMap() } val request = ToolRequest( invocationId = invocationId, @@ -405,6 +411,26 @@ abstract class SessionOrchestrator( ) } + private suspend fun emitToolArtifacts( + sessionId: SessionId, + stageId: StageId, + stageConfig: StageConfig, + ) { + stageConfig.produces + .filter { !it.kind.llmEmitted } + .forEach { slot -> + emit( + sessionId, + ArtifactCreatedEvent( + artifactId = slot.name, + sessionId = sessionId, + stageId = stageId, + schemaVersion = 1, + ), + ) + } + } + private fun verifyProduces( sessionId: SessionId, stageId: StageId, @@ -412,7 +438,9 @@ abstract class SessionOrchestrator( ): StageExecutionResult { if (stageConfig.produces.isEmpty()) return StageExecutionResult.Success(emptyList()) val producedIds = stageConfig.produces.map { it.name }.toSet() - val present = artifactRepository.getByIds(sessionId, producedIds).keys + val present = eventStore.read(sessionId) + .mapNotNull { (it.payload as? ArtifactCreatedEvent)?.artifactId } + .toSet() val missing = producedIds - present if (missing.isEmpty()) return StageExecutionResult.Success(emptyList()) val missingIds = missing.joinToString(", ") { it.value }