feat(kernel): inject needs-artifact content into stage context
Stages declaring needs=[...] now receive each needed artifact's validated JSON (from artifactContentCache) as an L1/USER neededArtifact context entry, so a one-shot subagent sees its inputs. Also emit llm-emitted artifact lifecycle events (Created/Validating/Validated) on successful validation via emitLlmArtifacts, closing a latent gap where verifyProduces would fail for stages producing only an llm-emitted artifact. Events are emitted only after validation passes, preserving invariant #7.
This commit is contained in:
+37
-1
@@ -306,8 +306,9 @@ abstract class SessionOrchestrator(
|
||||
),
|
||||
)
|
||||
val repoMapEntries = buildRepoMapEntries(sessionId)
|
||||
val needsEntries = buildNeedsArtifactEntries(sessionId, stageConfig)
|
||||
var accumulatedEntries =
|
||||
systemPrompt + journalEntries + repoMapEntries + schemaEntries + promptEntries + steeringEntries
|
||||
systemPrompt + journalEntries + repoMapEntries + needsEntries + schemaEntries + promptEntries + steeringEntries
|
||||
val contextPack = contextPackBuilder.build(
|
||||
id = ContextPackId(UUID.randomUUID().toString()),
|
||||
sessionId = sessionId,
|
||||
@@ -384,6 +385,7 @@ abstract class SessionOrchestrator(
|
||||
when (val outcome = mapValidationOutcome(sessionId, stageId, validationCtx)) {
|
||||
is StageExecutionResult.Success -> {
|
||||
emitToolArtifacts(sessionId, stageId, stageConfig)
|
||||
emitLlmArtifacts(sessionId, stageId, stageConfig)
|
||||
verifyProduces(sessionId, stageId, stageConfig)
|
||||
}
|
||||
|
||||
@@ -843,6 +845,26 @@ abstract class SessionOrchestrator(
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun buildNeedsArtifactEntries(
|
||||
sessionId: SessionId,
|
||||
stageConfig: StageConfig,
|
||||
): List<ContextEntry> {
|
||||
return stageConfig.needs.mapNotNull { needed ->
|
||||
val cached = artifactContentCache["${sessionId.value}:${needed.value}"]
|
||||
?.takeIf { it.isNotBlank() } ?: return@mapNotNull null
|
||||
val content = "## Input artifact: ${needed.value}\n$cached"
|
||||
ContextEntry(
|
||||
id = ContextEntryId(UUID.randomUUID().toString()),
|
||||
layer = ContextLayer.L1,
|
||||
content = content,
|
||||
sourceType = "neededArtifact",
|
||||
sourceId = needed.value,
|
||||
tokenEstimate = estimateTokens(content),
|
||||
role = EntryRole.USER,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun emitToolArtifacts(
|
||||
sessionId: SessionId,
|
||||
stageId: StageId,
|
||||
@@ -857,6 +879,20 @@ abstract class SessionOrchestrator(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun emitLlmArtifacts(
|
||||
sessionId: SessionId,
|
||||
stageId: StageId,
|
||||
stageConfig: StageConfig,
|
||||
) {
|
||||
stageConfig.produces
|
||||
.filter { it.kind.llmEmitted }
|
||||
.forEach { slot ->
|
||||
emit(sessionId, ArtifactCreatedEvent(slot.name, sessionId, stageId, schemaVersion = 1))
|
||||
emit(sessionId, ArtifactValidatingEvent(slot.name, sessionId, stageId))
|
||||
emit(sessionId, ArtifactValidatedEvent(slot.name, sessionId, stageId))
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyProduces(
|
||||
sessionId: SessionId,
|
||||
stageId: StageId,
|
||||
|
||||
Reference in New Issue
Block a user