epic-12: after epic audit and init commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
id 'org.jetbrains.kotlin.plugin.serialization'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-datetime"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.correx.core.approvals
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class ApprovalOutcome {
|
||||
APPROVED,
|
||||
REJECTED,
|
||||
AUTO_APPROVED,
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.correx.core.approvals
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed interface ApprovalStatus {
|
||||
@Serializable data object PENDING : ApprovalStatus
|
||||
@Serializable data object COMPLETED : ApprovalStatus
|
||||
@Serializable data object TIMED_OUT : ApprovalStatus
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.correx.core.approvals
|
||||
|
||||
import com.correx.core.events.types.ProjectId
|
||||
import com.correx.core.events.types.StageId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed interface GrantScope {
|
||||
@Serializable data object SESSION : GrantScope
|
||||
@Serializable data class STAGE(val stageId: StageId) : GrantScope
|
||||
@Serializable data class PROJECT(val projectId: ProjectId) : GrantScope
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.correx.core.approvals
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Serializable
|
||||
enum class Tier(val level: Int) {
|
||||
T0(0),
|
||||
T1(1),
|
||||
T2(2),
|
||||
T3(3),
|
||||
T4(4)
|
||||
}
|
||||
|
||||
fun Tier.isAtLeast(other: Tier): Boolean = this.level >= other.level
|
||||
fun Tier.isAtMost(other: Tier): Boolean = this.level <= other.level
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.correx.core.approvals
|
||||
|
||||
import com.correx.core.events.types.SessionId
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UserSteering(
|
||||
val text: String,
|
||||
val sessionId: SessionId,
|
||||
val timestamp: Instant
|
||||
)
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.correx.core.events
|
||||
|
||||
import com.correx.core.events.events.EventMetadata
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import com.correx.core.events.events.NewEvent
|
||||
import com.correx.core.events.stores.EventStore
|
||||
import com.correx.core.events.types.CausationId
|
||||
import com.correx.core.events.types.CorrelationId
|
||||
import com.correx.core.events.types.EventId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import kotlinx.datetime.Clock
|
||||
import java.util.UUID
|
||||
|
||||
class EventDispatcher(private val eventStore: EventStore) {
|
||||
suspend fun emit(
|
||||
payload: EventPayload,
|
||||
sessionId: SessionId,
|
||||
causationId: CausationId? = null,
|
||||
correlationId: CorrelationId? = null,
|
||||
) {
|
||||
val event = NewEvent(
|
||||
metadata = EventMetadata(
|
||||
eventId = EventId(UUID.randomUUID().toString()),
|
||||
sessionId = sessionId,
|
||||
timestamp = Clock.System.now(),
|
||||
schemaVersion = 1,
|
||||
causationId = causationId,
|
||||
correlationId = correlationId,
|
||||
),
|
||||
payload = payload
|
||||
)
|
||||
eventStore.append(event)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.approvals.ApprovalOutcome
|
||||
import com.correx.core.approvals.ApprovalStatus
|
||||
import com.correx.core.approvals.GrantScope
|
||||
import com.correx.core.approvals.Tier
|
||||
import com.correx.core.approvals.UserSteering
|
||||
import com.correx.core.events.types.ApprovalDecisionId
|
||||
import com.correx.core.events.types.ApprovalRequestId
|
||||
import com.correx.core.events.types.GrantId
|
||||
import com.correx.core.events.types.ProjectId
|
||||
import com.correx.core.events.types.RiskSummaryId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import com.correx.core.events.types.ValidationReportId
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ApprovalRequestedEvent(
|
||||
val requestId: ApprovalRequestId,
|
||||
val tier: Tier,
|
||||
val validationReportId: ValidationReportId,
|
||||
val riskSummaryId: RiskSummaryId?,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId?,
|
||||
val projectId: ProjectId?,
|
||||
val userSteering: UserSteering? = null
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ApprovalDecisionResolvedEvent(
|
||||
val decisionId: ApprovalDecisionId,
|
||||
val requestId: ApprovalRequestId,
|
||||
val outcome: ApprovalOutcome,
|
||||
val status: ApprovalStatus,
|
||||
val tier: Tier,
|
||||
val resolutionTimestamp: Instant,
|
||||
val reason: String?,
|
||||
val userSteering: UserSteering? = null
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ApprovalGrantCreatedEvent(
|
||||
val grantId: GrantId,
|
||||
val scope: GrantScope,
|
||||
val permittedTiers: Set<Tier>,
|
||||
val reason: String,
|
||||
val expiresAt: Instant?,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId?,
|
||||
val projectId: ProjectId?
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ApprovalGrantExpiredEvent(
|
||||
val grantId: GrantId
|
||||
) : EventPayload
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.types.ArtifactId
|
||||
import com.correx.core.events.types.ArtifactRelationshipType
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ArtifactValidatingEvent(
|
||||
val artifactId: ArtifactId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ArtifactValidatedEvent(
|
||||
val artifactId: ArtifactId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ArtifactSupersededEvent(
|
||||
val artifactId: ArtifactId,
|
||||
val supersededById: ArtifactId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ArtifactRelationshipAddedEvent(
|
||||
val sourceId: ArtifactId,
|
||||
val targetId: ArtifactId,
|
||||
val relationshipType: ArtifactRelationshipType,
|
||||
val sessionId: SessionId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ArtifactRejectedEvent(
|
||||
val artifactId: ArtifactId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val reason: String,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ArtifactCreatedEvent(
|
||||
val artifactId: ArtifactId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val schemaVersion: Int,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ArtifactArchivedEvent(
|
||||
val artifactId: ArtifactId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
) : EventPayload
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.types.ContextPackId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class LayerTruncatedEvent(
|
||||
val contextPackId: ContextPackId,
|
||||
val layer: String,
|
||||
val entriesDropped: Int,
|
||||
val reason: String,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ContextBuildingStartedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ContextBuildingFailedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val reason: String,
|
||||
) : EventPayload
|
||||
|
||||
// Emitted during replay when a session ended with buildingInProgress=true and no completion/failure event followed.
|
||||
@Serializable
|
||||
data class ContextBuildingInterruptedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class CompressionAppliedEvent(
|
||||
val contextPackId: ContextPackId,
|
||||
val layer: String,
|
||||
val entriesRemoved: Int,
|
||||
val strategyApplied: String,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ContextPackBuiltEvent(
|
||||
val contextPackId: ContextPackId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val budgetUsed: Int,
|
||||
val budgetLimit: Int,
|
||||
) : EventPayload
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class NewEvent(
|
||||
val metadata: EventMetadata,
|
||||
val payload: EventPayload
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class StoredEvent(
|
||||
val metadata: EventMetadata,
|
||||
val sequence: Long,
|
||||
val payload: EventPayload
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.types.CausationId
|
||||
import com.correx.core.events.types.CorrelationId
|
||||
import com.correx.core.events.types.EventId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class EventMetadata(
|
||||
val eventId: EventId,
|
||||
val sessionId: SessionId,
|
||||
val timestamp: Instant,
|
||||
val schemaVersion: Int,
|
||||
val causationId: CausationId?,
|
||||
val correlationId: CorrelationId?
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed interface EventPayload
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.types.InferenceRequestId
|
||||
import com.correx.core.events.types.ProviderId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import com.correx.core.inference.TokenUsage
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
||||
@Serializable
|
||||
data class InferenceStartedEvent(
|
||||
val requestId: InferenceRequestId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val providerId: ProviderId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class InferenceCompletedEvent(
|
||||
val requestId: InferenceRequestId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val providerId: ProviderId,
|
||||
val tokensUsed: TokenUsage,
|
||||
val latencyMs: Long,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class InferenceFailedEvent(
|
||||
val requestId: InferenceRequestId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val providerId: ProviderId,
|
||||
val reason: String, // human-readable; structured cause lives in CancellationReason
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class InferenceTimeoutEvent(
|
||||
val requestId: InferenceRequestId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val providerId: ProviderId,
|
||||
val timeoutMs: Long,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ModelLoadedEvent(
|
||||
val modelId: String,
|
||||
val providerId: ProviderId,
|
||||
val sessionId: SessionId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ModelUnloadedEvent(
|
||||
val modelId: String,
|
||||
val providerId: ProviderId,
|
||||
val sessionId: SessionId,
|
||||
val cancellationReason: String? = null, // serialized label, not the sealed class
|
||||
) : EventPayload
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.execution.RetryPolicy
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class WorkflowStartedEvent(
|
||||
val sessionId: SessionId,
|
||||
val startStageId: StageId,
|
||||
val retryPolicy: RetryPolicy? = null,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class WorkflowCompletedEvent(
|
||||
val sessionId: SessionId,
|
||||
val terminalStageId: StageId,
|
||||
val totalStages: Int,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class WorkflowFailedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val reason: String,
|
||||
val retryExhausted: Boolean,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class OrchestrationPausedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val reason: String, // e.g. "APPROVAL_PENDING", "USER_REQUESTED"
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class OrchestrationResumedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class RetryAttemptedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val attemptNumber: Int,
|
||||
val maxAttempts: Int,
|
||||
val failureReason: String,
|
||||
) : EventPayload
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.risk.RiskAction
|
||||
import com.correx.core.events.risk.RiskLevel
|
||||
import com.correx.core.events.types.RiskSummaryId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class RiskAssessedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val riskSummaryId: RiskSummaryId,
|
||||
val level: RiskLevel,
|
||||
val action: RiskAction,
|
||||
) : EventPayload
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.types.SessionId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SessionStartedEvent(
|
||||
val sessionId: SessionId,
|
||||
val initialContextId: String? = null
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class SessionPausedEvent(
|
||||
val sessionId: SessionId,
|
||||
val reason: String? = null
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class SessionResumedEvent(
|
||||
val sessionId: SessionId,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class SessionCompletedEvent(
|
||||
val sessionId: SessionId,
|
||||
val summary: String? = null
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class SessionFailedEvent(
|
||||
val sessionId: SessionId,
|
||||
val errorCode: String? = null,
|
||||
val errorMessage: String? = null
|
||||
) : EventPayload
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import com.correx.core.events.types.TransitionId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class StageStartedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val transitionId: TransitionId
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class StageCompletedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val transitionId: TransitionId
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class StageFailedEvent(
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val transitionId: TransitionId,
|
||||
val reason: String
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class TransitionExecutedEvent(
|
||||
val sessionId: SessionId,
|
||||
val from: StageId,
|
||||
val to: StageId,
|
||||
val transitionId: TransitionId
|
||||
) : EventPayload
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.approvals.Tier
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import com.correx.core.events.types.ToolInvocationId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ToolExecutionCompletedEvent(
|
||||
val invocationId: ToolInvocationId,
|
||||
val sessionId: SessionId,
|
||||
val toolName: String,
|
||||
val receipt: ToolReceipt,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ToolExecutionFailedEvent(
|
||||
val invocationId: ToolInvocationId,
|
||||
val sessionId: SessionId,
|
||||
val toolName: String,
|
||||
val reason: String,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ToolExecutionRejectedEvent(
|
||||
val invocationId: ToolInvocationId,
|
||||
val sessionId: SessionId,
|
||||
val toolName: String,
|
||||
val tier: Tier,
|
||||
val reason: String,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ToolExecutionStartedEvent(
|
||||
val invocationId: ToolInvocationId,
|
||||
val sessionId: SessionId,
|
||||
val toolName: String,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ToolInvocationRequestedEvent(
|
||||
val invocationId: ToolInvocationId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val toolName: String,
|
||||
val tier: Tier,
|
||||
val request: ToolRequest,
|
||||
) : EventPayload
|
||||
|
||||
@Serializable
|
||||
data class ToolInvokedEvent(
|
||||
val toolId: String,
|
||||
) : EventPayload
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.approvals.Tier
|
||||
import com.correx.core.events.serialization.AnyMapSerializer
|
||||
import com.correx.core.events.types.ToolInvocationId
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ToolReceipt(
|
||||
val invocationId: ToolInvocationId,
|
||||
val toolName: String,
|
||||
val exitCode: Int,
|
||||
val outputSummary: String,
|
||||
@Serializable(with = AnyMapSerializer::class)
|
||||
val structuredOutput: Map<String, Any> = emptyMap(),
|
||||
val affectedEntities: List<String> = emptyList(),
|
||||
val durationMs: Long,
|
||||
val tier: Tier,
|
||||
val timestamp: Instant
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.correx.core.events.events
|
||||
|
||||
import com.correx.core.events.serialization.AnyMapSerializer
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import com.correx.core.events.types.ToolInvocationId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ToolRequest(
|
||||
val invocationId: ToolInvocationId,
|
||||
val sessionId: SessionId,
|
||||
val stageId: StageId,
|
||||
val toolName: String,
|
||||
@Serializable(with = AnyMapSerializer::class)
|
||||
val parameters: Map<String, Any> = emptyMap()
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.correx.core.events.execution
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class RetryPolicy(
|
||||
val maxAttempts: Int,
|
||||
val backoffMs: Long = 0L,
|
||||
) {
|
||||
init {
|
||||
require(maxAttempts >= 1) { "maxAttempts must be >= 1" }
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.correx.core.events.orchestration
|
||||
|
||||
import com.correx.core.events.execution.RetryPolicy
|
||||
import com.correx.core.events.types.StageId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class OrchestrationState(
|
||||
val currentStageId: StageId? = null,
|
||||
val status: OrchestrationStatus = OrchestrationStatus.IDLE,
|
||||
val retryCount: Int = 0,
|
||||
val pauseReason: String? = null,
|
||||
val pendingApproval: Boolean = false,
|
||||
val failureReason: String? = null,
|
||||
val retryPolicy: RetryPolicy? = null,
|
||||
)
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package com.correx.core.events.orchestration
|
||||
|
||||
enum class OrchestrationStatus {
|
||||
IDLE,
|
||||
RUNNING,
|
||||
PAUSED,
|
||||
COMPLETED,
|
||||
FAILED,
|
||||
CANCELED,
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.correx.core.events.risk
|
||||
|
||||
enum class RiskAction { PROCEED, PROMPT_USER, BLOCK }
|
||||
@@ -0,0 +1,3 @@
|
||||
package com.correx.core.events.risk
|
||||
|
||||
enum class RiskLevel { LOW, MEDIUM, HIGH, CRITICAL }
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.correx.core.events.risk
|
||||
|
||||
sealed class RiskSignal {
|
||||
data class CycleWithoutExit(val cycleId: String) : RiskSignal()
|
||||
data class RepeatedFailure(val reason: String, val count: Int) : RiskSignal()
|
||||
data class ValidationErrors(val errorCount: Int) : RiskSignal()
|
||||
data class InferenceTimeout(val elapsedMs: Long) : RiskSignal()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.correx.core.events.risk
|
||||
|
||||
data class RiskSummary(
|
||||
val level: RiskLevel,
|
||||
val signals: List<RiskSignal>,
|
||||
val recommendedAction: RiskAction,
|
||||
)
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.correx.core.events.serialization
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.builtins.MapSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonDecoder
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonEncoder
|
||||
import kotlinx.serialization.json.JsonNull
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
|
||||
private object AnySerializer : KSerializer<Any> {
|
||||
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("AnyValue")
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Any) {
|
||||
val jsonEncoder = encoder as? JsonEncoder
|
||||
?: throw SerializationException("AnySerializer requires a JSON encoder")
|
||||
jsonEncoder.encodeJsonElement(value.toJsonElement())
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Any {
|
||||
val jsonDecoder = decoder as? JsonDecoder
|
||||
?: throw SerializationException("AnySerializer requires a JSON decoder")
|
||||
return jsonDecoder.decodeJsonElement().toAny()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Any?.toJsonElement(): JsonElement = when (this) {
|
||||
null -> JsonNull
|
||||
is String -> JsonPrimitive(this)
|
||||
is Number -> JsonPrimitive(this)
|
||||
is Boolean -> JsonPrimitive(this)
|
||||
is Map<*, *> -> JsonObject(entries.associate { (k, v) -> k.toString() to v.toJsonElement() })
|
||||
is List<*> -> JsonArray(map { it.toJsonElement() })
|
||||
else -> throw SerializationException("Unsupported type: ${this::class.simpleName}")
|
||||
}
|
||||
|
||||
private fun JsonElement.toAny(): Any = when (this) {
|
||||
is JsonNull -> throw SerializationException("null values are not supported in Map<String, Any>")
|
||||
is JsonPrimitive -> {
|
||||
val content = this.content
|
||||
when {
|
||||
content == "true" || content == "false" -> content == "true"
|
||||
content == "null" -> throw SerializationException("null values are not supported in Map<String, Any>")
|
||||
content.toDoubleOrNull() != null && content.contains('.') -> content.toDouble()
|
||||
content.toLongOrNull() != null -> content.toLong()
|
||||
else -> content // fallback to string
|
||||
}
|
||||
}
|
||||
is JsonObject -> entries.associate { (k, v) -> k to v.toAny() }
|
||||
is JsonArray -> map { it.toAny() }
|
||||
}
|
||||
|
||||
object AnyMapSerializer : KSerializer<Map<String, Any>> {
|
||||
private val delegate = MapSerializer(String.serializer(), AnySerializer)
|
||||
override val descriptor: SerialDescriptor = delegate.descriptor
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun deserialize(decoder: Decoder): Map<String, Any> =
|
||||
delegate.deserialize(decoder) as Map<String, Any>
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Map<String, Any>) =
|
||||
delegate.serialize(encoder, value)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.correx.core.events.serialization
|
||||
|
||||
import com.correx.core.events.events.EventPayload
|
||||
|
||||
interface EventSerializer {
|
||||
fun serialize(payload: EventPayload): String
|
||||
fun deserialize(raw: String): EventPayload
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.correx.core.events.serialization
|
||||
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class JsonEventSerializer(
|
||||
private val json: Json = eventJson
|
||||
) : EventSerializer {
|
||||
|
||||
override fun serialize(payload: EventPayload): String =
|
||||
json.encodeToString(EventPayload.serializer(), payload)
|
||||
|
||||
override fun deserialize(raw: String): EventPayload =
|
||||
json.decodeFromString(EventPayload.serializer(), raw)
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.correx.core.events.serialization
|
||||
|
||||
import com.correx.core.events.events.ApprovalDecisionResolvedEvent
|
||||
import com.correx.core.events.events.ApprovalGrantCreatedEvent
|
||||
import com.correx.core.events.events.ApprovalGrantExpiredEvent
|
||||
import com.correx.core.events.events.ApprovalRequestedEvent
|
||||
import com.correx.core.events.events.ArtifactArchivedEvent
|
||||
import com.correx.core.events.events.ArtifactCreatedEvent
|
||||
import com.correx.core.events.events.ArtifactRejectedEvent
|
||||
import com.correx.core.events.events.ArtifactRelationshipAddedEvent
|
||||
import com.correx.core.events.events.ArtifactSupersededEvent
|
||||
import com.correx.core.events.events.ArtifactValidatedEvent
|
||||
import com.correx.core.events.events.ArtifactValidatingEvent
|
||||
import com.correx.core.events.events.CompressionAppliedEvent
|
||||
import com.correx.core.events.events.ContextBuildingFailedEvent
|
||||
import com.correx.core.events.events.ContextBuildingInterruptedEvent
|
||||
import com.correx.core.events.events.ContextBuildingStartedEvent
|
||||
import com.correx.core.events.events.ContextPackBuiltEvent
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import com.correx.core.events.events.InferenceCompletedEvent
|
||||
import com.correx.core.events.events.InferenceFailedEvent
|
||||
import com.correx.core.events.events.InferenceStartedEvent
|
||||
import com.correx.core.events.events.InferenceTimeoutEvent
|
||||
import com.correx.core.events.events.ModelLoadedEvent
|
||||
import com.correx.core.events.events.ModelUnloadedEvent
|
||||
import com.correx.core.events.events.LayerTruncatedEvent
|
||||
import com.correx.core.events.events.OrchestrationPausedEvent
|
||||
import com.correx.core.events.events.OrchestrationResumedEvent
|
||||
import com.correx.core.events.events.RetryAttemptedEvent
|
||||
import com.correx.core.events.events.RiskAssessedEvent
|
||||
import com.correx.core.events.events.SessionCompletedEvent
|
||||
import com.correx.core.events.events.SessionFailedEvent
|
||||
import com.correx.core.events.events.SessionPausedEvent
|
||||
import com.correx.core.events.events.SessionResumedEvent
|
||||
import com.correx.core.events.events.SessionStartedEvent
|
||||
import com.correx.core.events.events.StageCompletedEvent
|
||||
import com.correx.core.events.events.StageFailedEvent
|
||||
import com.correx.core.events.events.StageStartedEvent
|
||||
import com.correx.core.events.events.ToolExecutionCompletedEvent
|
||||
import com.correx.core.events.events.ToolExecutionFailedEvent
|
||||
import com.correx.core.events.events.ToolExecutionRejectedEvent
|
||||
import com.correx.core.events.events.ToolExecutionStartedEvent
|
||||
import com.correx.core.events.events.ToolInvokedEvent
|
||||
import com.correx.core.events.events.ToolInvocationRequestedEvent
|
||||
import com.correx.core.events.events.TransitionExecutedEvent
|
||||
import com.correx.core.events.events.WorkflowCompletedEvent
|
||||
import com.correx.core.events.events.WorkflowFailedEvent
|
||||
import com.correx.core.events.events.WorkflowStartedEvent
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.polymorphic
|
||||
import kotlinx.serialization.modules.subclass
|
||||
|
||||
val eventModule = SerializersModule {
|
||||
polymorphic(EventPayload::class) {
|
||||
// Legacy stub event — predates the full tool lifecycle model (ToolInvocationRequestedEvent et al.)
|
||||
subclass(ToolInvokedEvent::class)
|
||||
subclass(ToolInvocationRequestedEvent::class)
|
||||
subclass(ToolExecutionStartedEvent::class)
|
||||
subclass(ToolExecutionCompletedEvent::class)
|
||||
subclass(ToolExecutionFailedEvent::class)
|
||||
subclass(ToolExecutionRejectedEvent::class)
|
||||
subclass(SessionStartedEvent::class)
|
||||
subclass(SessionPausedEvent::class)
|
||||
subclass(SessionResumedEvent::class)
|
||||
subclass(SessionCompletedEvent::class)
|
||||
subclass(SessionFailedEvent::class)
|
||||
subclass(StageStartedEvent::class)
|
||||
subclass(StageFailedEvent::class)
|
||||
subclass(StageCompletedEvent::class)
|
||||
subclass(TransitionExecutedEvent::class)
|
||||
subclass(ApprovalRequestedEvent::class)
|
||||
subclass(ApprovalDecisionResolvedEvent::class)
|
||||
subclass(ApprovalGrantCreatedEvent::class)
|
||||
subclass(ApprovalGrantExpiredEvent::class)
|
||||
subclass(ContextBuildingStartedEvent::class)
|
||||
subclass(ContextPackBuiltEvent::class)
|
||||
subclass(CompressionAppliedEvent::class)
|
||||
subclass(LayerTruncatedEvent::class)
|
||||
subclass(ContextBuildingFailedEvent::class)
|
||||
subclass(ContextBuildingInterruptedEvent::class)
|
||||
subclass(ArtifactCreatedEvent::class)
|
||||
subclass(ArtifactValidatingEvent::class)
|
||||
subclass(ArtifactValidatedEvent::class)
|
||||
subclass(ArtifactRejectedEvent::class)
|
||||
subclass(ArtifactSupersededEvent::class)
|
||||
subclass(ArtifactArchivedEvent::class)
|
||||
subclass(ArtifactRelationshipAddedEvent::class)
|
||||
subclass(InferenceFailedEvent::class)
|
||||
subclass(InferenceCompletedEvent::class)
|
||||
subclass(InferenceStartedEvent::class)
|
||||
subclass(InferenceTimeoutEvent::class)
|
||||
subclass(ModelLoadedEvent::class)
|
||||
subclass(ModelUnloadedEvent::class)
|
||||
subclass(OrchestrationResumedEvent::class)
|
||||
subclass(OrchestrationPausedEvent::class)
|
||||
subclass(WorkflowStartedEvent::class)
|
||||
subclass(WorkflowFailedEvent::class)
|
||||
subclass(WorkflowCompletedEvent::class)
|
||||
subclass(RetryAttemptedEvent::class)
|
||||
subclass(RiskAssessedEvent::class)
|
||||
}
|
||||
}
|
||||
|
||||
val eventJson = Json {
|
||||
serializersModule = eventModule
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.correx.core.events.stores
|
||||
|
||||
import com.correx.core.events.events.NewEvent
|
||||
import com.correx.core.events.events.StoredEvent
|
||||
import com.correx.core.events.types.SessionId
|
||||
|
||||
|
||||
interface EventStore {
|
||||
/**
|
||||
* Append a single event to the store.
|
||||
* Must enforce:
|
||||
* - monotonic per-session sequence
|
||||
* - idempotency by eventId
|
||||
* - causal consistency rules (if enforced here, otherwise in kernel)
|
||||
*/
|
||||
fun append(event: NewEvent): StoredEvent
|
||||
|
||||
/**
|
||||
* Append multiple events atomically (same session preferred).
|
||||
*/
|
||||
fun appendAll(events: List<NewEvent>): List<StoredEvent>
|
||||
|
||||
/**
|
||||
* Read events for a session in strict sequence order.
|
||||
*/
|
||||
fun read(sessionId: SessionId): List<StoredEvent>
|
||||
|
||||
/**
|
||||
* Read events from a specific sequence offset (for replay/resume).
|
||||
*/
|
||||
fun readFrom(sessionId: SessionId, fromSequence: Long): List<StoredEvent>
|
||||
|
||||
/**
|
||||
* Get last sequence number for session.
|
||||
*/
|
||||
fun lastSequence(sessionId: SessionId): Long?
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.correx.core.events.types
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class ArtifactLifecyclePhase {
|
||||
CREATED,
|
||||
VALIDATING,
|
||||
VALIDATED,
|
||||
REJECTED,
|
||||
SUPERSEDED,
|
||||
ARCHIVED
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.correx.core.events.types
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
enum class ArtifactRelationshipType {
|
||||
PARENT,
|
||||
CHILD,
|
||||
SUPERSEDES,
|
||||
DERIVED_FROM,
|
||||
VALIDATED_BY,
|
||||
APPROVED_BY,
|
||||
GENERATED_FROM
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.correx.core.events.types
|
||||
|
||||
object EventTypes {
|
||||
const val SESSION_STARTED = "session.started"
|
||||
const val SESSION_PAUSED = "session.paused"
|
||||
const val SESSION_COMPLETED = "session.completed"
|
||||
|
||||
const val TOOL_INVOKED = "tool.invoked"
|
||||
const val TOOL_COMPLETED = "tool.completed"
|
||||
|
||||
const val APPROVAL_REQUESTED = "approval.requested"
|
||||
const val APPROVAL_DECISION_RESOLVED = "approval.decision_resolved"
|
||||
const val APPROVAL_GRANT_CREATED = "approval.grant_created"
|
||||
const val APPROVAL_GRANT_EXPIRED = "approval.grant_expired"
|
||||
|
||||
const val ARTIFACT_CREATED = "artifact.created"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.correx.core.events.types
|
||||
|
||||
import com.correx.core.utils.TypeId
|
||||
|
||||
|
||||
// Events types
|
||||
typealias EventId = TypeId
|
||||
typealias SessionId = TypeId
|
||||
typealias CorrelationId = TypeId
|
||||
typealias CausationId = TypeId
|
||||
|
||||
|
||||
// Transitions types
|
||||
typealias StageId = TypeId
|
||||
typealias TransitionId = TypeId
|
||||
|
||||
|
||||
// Artifacts types
|
||||
typealias ArtifactId = TypeId
|
||||
|
||||
|
||||
// Context types
|
||||
typealias ContextPackId = TypeId
|
||||
typealias ContextEntryId = TypeId
|
||||
|
||||
|
||||
// Approvals types
|
||||
typealias ValidationReportId = TypeId
|
||||
typealias RiskSummaryId = TypeId
|
||||
typealias GrantId = TypeId
|
||||
typealias ApprovalRequestId = TypeId
|
||||
typealias ProjectId = TypeId
|
||||
typealias ApprovalDecisionId = TypeId
|
||||
|
||||
|
||||
// Tools types
|
||||
typealias ToolInvocationId = TypeId
|
||||
|
||||
|
||||
// Inference
|
||||
typealias InferenceRequestId = TypeId
|
||||
typealias ProviderId = TypeId
|
||||
@@ -0,0 +1 @@
|
||||
package com.correx.core.events.types
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.correx.core.inference
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class TokenUsage(
|
||||
val promptTokens: Int,
|
||||
val completionTokens: Int,
|
||||
) {
|
||||
val totalTokens: Int get() = promptTokens + completionTokens
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.correx.core.sessions.projections
|
||||
|
||||
import com.correx.core.events.events.StoredEvent
|
||||
|
||||
class DefaultStateBuilder<S>(
|
||||
private val projection: Projection<S>
|
||||
) : StateBuilder<S> {
|
||||
|
||||
override fun build(events: List<StoredEvent>): S {
|
||||
return events.fold(projection.initial()) { state, event ->
|
||||
projection.apply(state, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.correx.core.sessions.projections
|
||||
|
||||
import com.correx.core.events.events.StoredEvent
|
||||
|
||||
interface Projection<S> {
|
||||
|
||||
fun initial(): S
|
||||
|
||||
fun apply(state: S, event: StoredEvent): S
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.correx.core.sessions.projections
|
||||
|
||||
import com.correx.core.events.events.StoredEvent
|
||||
|
||||
interface StateBuilder<S> {
|
||||
fun build(events: List<StoredEvent>): S
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.correx.core.sessions.projections.replay
|
||||
|
||||
import com.correx.core.events.stores.EventStore
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.sessions.projections.DefaultStateBuilder
|
||||
import com.correx.core.sessions.projections.Projection
|
||||
|
||||
class DefaultEventReplayer<S>(
|
||||
private val store: EventStore,
|
||||
private val projection: Projection<S>
|
||||
) : EventReplayer<S> {
|
||||
|
||||
override fun rebuild(sessionId: SessionId): S {
|
||||
val events = store.read(sessionId)
|
||||
|
||||
return DefaultStateBuilder(projection)
|
||||
.build(events)
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package com.correx.core.sessions.projections.replay
|
||||
|
||||
import com.correx.core.events.types.SessionId
|
||||
|
||||
interface EventReplayer<S> {
|
||||
fun rebuild(sessionId: SessionId): S
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.correx.core.utils
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@JvmInline
|
||||
@Serializable
|
||||
value class TypeId(val value: String) {
|
||||
init {
|
||||
require(value.isNotBlank()) { "id must not be blank" }
|
||||
require(value.trim() == value) { "id must not contain leading/trailing whitespace" }
|
||||
}
|
||||
|
||||
override fun toString(): String = value
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.correx.core.events
|
||||
|
||||
import com.correx.core.approvals.Tier
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import com.correx.core.events.events.ToolExecutionCompletedEvent
|
||||
import com.correx.core.events.events.ToolExecutionFailedEvent
|
||||
import com.correx.core.events.events.ToolExecutionRejectedEvent
|
||||
import com.correx.core.events.events.ToolExecutionStartedEvent
|
||||
import com.correx.core.events.events.ToolInvocationRequestedEvent
|
||||
import com.correx.core.events.events.ToolReceipt
|
||||
import com.correx.core.events.events.ToolRequest
|
||||
import com.correx.core.events.serialization.eventJson
|
||||
import com.correx.core.events.types.SessionId
|
||||
import com.correx.core.events.types.StageId
|
||||
import com.correx.core.events.types.ToolInvocationId
|
||||
import kotlinx.datetime.Instant
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class ToolEventsSerializationTest {
|
||||
private val invocationId = ToolInvocationId("inv-1")
|
||||
private val sessionId = SessionId("s-1")
|
||||
private val stageId = StageId("st-1")
|
||||
|
||||
private val request = ToolRequest(
|
||||
invocationId = invocationId,
|
||||
sessionId = sessionId,
|
||||
stageId = stageId,
|
||||
toolName = "echo",
|
||||
parameters = mapOf("key" to "value", "count" to 42L)
|
||||
)
|
||||
|
||||
private val receipt = ToolReceipt(
|
||||
invocationId = invocationId,
|
||||
toolName = "echo",
|
||||
exitCode = 0,
|
||||
outputSummary = "done",
|
||||
structuredOutput = mapOf("result" to "ok"),
|
||||
affectedEntities = listOf("file.txt"),
|
||||
durationMs = 100L,
|
||||
tier = Tier.T0,
|
||||
timestamp = Instant.parse("2026-01-01T00:00:00Z")
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `ToolInvocationRequestedEvent round-trips`() {
|
||||
val event = ToolInvocationRequestedEvent(invocationId, sessionId, stageId, "echo", Tier.T0, request)
|
||||
val json = eventJson.encodeToString(ToolInvocationRequestedEvent.serializer(), event)
|
||||
assertEquals(event, eventJson.decodeFromString(ToolInvocationRequestedEvent.serializer(), json))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ToolExecutionStartedEvent round-trips`() {
|
||||
val event = ToolExecutionStartedEvent(invocationId, sessionId, "echo")
|
||||
val json = eventJson.encodeToString(ToolExecutionStartedEvent.serializer(), event)
|
||||
assertEquals(event, eventJson.decodeFromString(ToolExecutionStartedEvent.serializer(), json))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ToolExecutionCompletedEvent round-trips`() {
|
||||
val event = ToolExecutionCompletedEvent(invocationId, sessionId, "echo", receipt)
|
||||
val json = eventJson.encodeToString(ToolExecutionCompletedEvent.serializer(), event)
|
||||
assertEquals(event, eventJson.decodeFromString(ToolExecutionCompletedEvent.serializer(), json))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ToolExecutionFailedEvent round-trips`() {
|
||||
val event = ToolExecutionFailedEvent(invocationId, sessionId, "echo", "something broke")
|
||||
val json = eventJson.encodeToString(ToolExecutionFailedEvent.serializer(), event)
|
||||
assertEquals(event, eventJson.decodeFromString(ToolExecutionFailedEvent.serializer(), json))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ToolExecutionRejectedEvent round-trips`() {
|
||||
val event = ToolExecutionRejectedEvent(invocationId, sessionId, "echo", Tier.T4, "tier too high")
|
||||
val json = eventJson.encodeToString(ToolExecutionRejectedEvent.serializer(), event)
|
||||
assertEquals(event, eventJson.decodeFromString(ToolExecutionRejectedEvent.serializer(), json))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ToolRequest with parameters round-trips`() {
|
||||
val json = eventJson.encodeToString(ToolRequest.serializer(), request)
|
||||
assertEquals(request, eventJson.decodeFromString(ToolRequest.serializer(), json))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ToolReceipt with structuredOutput round-trips`() {
|
||||
val json = eventJson.encodeToString(ToolReceipt.serializer(), receipt)
|
||||
assertEquals(receipt, eventJson.decodeFromString(ToolReceipt.serializer(), json))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ToolInvocationRequestedEvent round-trips through polymorphic EventPayload`() {
|
||||
val event = ToolInvocationRequestedEvent(invocationId, sessionId, stageId, "echo", Tier.T0, request)
|
||||
val json = eventJson.encodeToString(EventPayload.serializer(), event)
|
||||
val decoded = eventJson.decodeFromString(EventPayload.serializer(), json)
|
||||
assertEquals(event, decoded)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user