feat: tool-declared output compression (Tier A)
Tools can declare how their output is compressed before it enters the context pack, replacing uniform handling. Open-threads 6.3 / ADR-0003. - core:tools/compression: ToolOutputCompressor interface, IdentityCompressor default, declarative OutputCompressionSpec + CompressionRule (StripBlankLines, StripLeadingWhitespace, DropMatching, HeadTail), and the pure DeclarativeCompressor engine (regexes compiled at construction; bypassOnError passes raw on non-zero exit; never throws on input). - Tool gains `outputCompressor` (default IdentityCompressor). FileReadTool strips blanks + leading whitespace; ShellTool strips blanks + head/tail. - SessionOrchestrator applies the resolved tool's compressor on the ToolResult -> ContextEntry path only. Raw output stays authoritative in the event log (invariant #6); compression is a pure function of (raw, exitCode, spec), so no new event and replay stays deterministic. - Spec is @Serializable/config-shaped (JSON round-trip tested). No custom-tool-from-config path exists today, so first-party tools wire in code; TOML-declared compression rides along when that path lands. Tier B (format-aware parsers: failures-only test output, git-log oneline) is a documented per-tool extension behind the interface, not built here.
This commit is contained in:
+9
-4
@@ -82,6 +82,8 @@ import com.correx.core.risk.RiskAssessor
|
||||
import com.correx.core.risk.RiskContext
|
||||
import com.correx.core.risk.toApprovalTier
|
||||
import com.correx.core.sessions.Session
|
||||
import com.correx.core.tools.compression.ToolOutputContext
|
||||
import com.correx.core.tools.contract.Tool
|
||||
import com.correx.core.tools.contract.ToolExecutor
|
||||
import com.correx.core.tools.contract.ToolResult
|
||||
import com.correx.core.tools.registry.ToolRegistry
|
||||
@@ -365,7 +367,8 @@ abstract class SessionOrchestrator(
|
||||
toolName = toolCall.function.name,
|
||||
parameters = parameters,
|
||||
)
|
||||
val tier = toolRegistry?.resolve(toolCall.function.name)?.tier ?: Tier.T2
|
||||
val tool = toolRegistry?.resolve(toolCall.function.name)
|
||||
val tier = tool?.tier ?: Tier.T2
|
||||
emit(
|
||||
sessionId,
|
||||
ToolInvocationRequestedEvent(
|
||||
@@ -378,7 +381,7 @@ abstract class SessionOrchestrator(
|
||||
),
|
||||
)
|
||||
val plane2Risk: RiskSummary? = runPlane2Assessment(
|
||||
sessionId, stageId, invocationId, toolCall.function.name, request,
|
||||
sessionId, stageId, invocationId, toolCall.function.name, request, tool,
|
||||
)?.let { assessment ->
|
||||
if (assessment.recommendedAction == RiskAction.BLOCK) {
|
||||
emit(
|
||||
@@ -571,7 +574,9 @@ abstract class SessionOrchestrator(
|
||||
role = EntryRole.ASSISTANT,
|
||||
)
|
||||
val resultContent = when (result) {
|
||||
is ToolResult.Success -> result.output
|
||||
is ToolResult.Success ->
|
||||
tool?.outputCompressor?.compress(result.output, ToolOutputContext(result.exitCode))
|
||||
?: result.output
|
||||
is ToolResult.Failure -> {
|
||||
if (!result.recoverable) "FATAL: ${result.reason}"
|
||||
else "ERROR: ${result.reason}"
|
||||
@@ -596,10 +601,10 @@ abstract class SessionOrchestrator(
|
||||
invocationId: ToolInvocationId,
|
||||
toolName: String,
|
||||
request: ToolRequest,
|
||||
tool: Tool?,
|
||||
): RiskSummary? {
|
||||
val assessor = toolCallAssessor ?: return null
|
||||
val policy = workspacePolicy ?: return null
|
||||
val tool = toolRegistry?.resolve(toolName)
|
||||
val assessment = assessor.assess(
|
||||
ToolCallAssessmentInput(
|
||||
request = request,
|
||||
|
||||
Reference in New Issue
Block a user