feat(research): wire tools + research workflow graph (research-workflow §2/§3)
Makes the research feature runnable end-to-end, off by default. - config: [tools.research] (enabled, searxng_url, max_results, max_fetch_bytes). - registration: web_search/web_fetch are built into BOTH the default and per-workspace tool registries when research.enabled, sharing one HTTP client threaded from Main (none built on the static path). Egress stays harness-enforced: web_fetch is T2 (operator-approved) and the existing NetworkHostRule still applies. - workflow: examples/workflows/research.toml — decompose → gather → report, with the three artifact schemas and prompts. Fan-out (search per sub-question, fetch per source) runs as repeated tool calls inside the gather stage (Correx has no parallel agents); per-source synthesis into the dossier is the compression step, so the report stage consumes summaries, never raw pages. ResearchWorkflowTest validates the graph contract. To run: set [tools.research].enabled, register the 3 [[artifacts]], copy research.toml + prompts + schemas into the workflows dir, start SearXNG. Launch like any workflow (the T2 fetch approval surfaces as an approval card; the report opens in the artifact viewer). Follow-ups (noted, not blocking): batch fetch-approval at the source-list level (§3), a dedicated SourceFetched/LowQualityExtraction event (quality + content hash are already in tool-result metadata), dynamic per-session egress allowlist, and the web approval client (§6).
This commit is contained in:
@@ -195,12 +195,26 @@ fun main() {
|
||||
?.let { Path.of(it) }
|
||||
?: toolsConfig.workspaceRoot.takeIf { it.isNotEmpty() }?.let { Path.of(it) }
|
||||
?: workingDir
|
||||
// One shared HTTP client backs both the default and per-workspace registries' research tools
|
||||
// (web_search/web_fetch). Built only when research is enabled, so the static path stays offline.
|
||||
val researchToolConfig = com.correx.infrastructure.tools.ResearchToolConfig(
|
||||
enabled = toolsConfig.research.enabled,
|
||||
searxngUrl = toolsConfig.research.searxngUrl,
|
||||
maxResults = toolsConfig.research.maxResults,
|
||||
maxFetchBytes = toolsConfig.research.maxFetchBytes,
|
||||
httpClient = if (toolsConfig.research.enabled) {
|
||||
io.ktor.client.HttpClient(io.ktor.client.engine.cio.CIO)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
val toolRegistry = InfrastructureModule.createToolRegistry(
|
||||
buildToolConfig(
|
||||
workspaceRoot,
|
||||
workingDir,
|
||||
shellAllowedExecutables,
|
||||
toolsConfig,
|
||||
researchToolConfig,
|
||||
),
|
||||
)
|
||||
val toolExecutor = InfrastructureModule.createToolExecutor(
|
||||
@@ -228,7 +242,7 @@ fun main() {
|
||||
|
||||
val wsToolRegistryProvider = WorkspaceToolRegistryProvider { workspace ->
|
||||
val wsRegistry = InfrastructureModule.createToolRegistry(
|
||||
buildToolConfigForWorkspace(workspace, shellAllowedExecutables, toolsConfig),
|
||||
buildToolConfigForWorkspace(workspace, shellAllowedExecutables, toolsConfig, researchToolConfig),
|
||||
)
|
||||
val wsExecutor = DispatchingToolExecutor(wsRegistry)
|
||||
WorkspaceTools(registry = wsRegistry, executor = wsExecutor)
|
||||
@@ -626,6 +640,7 @@ private fun buildToolConfig(
|
||||
workingDir: Path,
|
||||
shellAllowedExecutables: Set<String>,
|
||||
toolsConfig: com.correx.core.config.ToolsConfig,
|
||||
research: com.correx.infrastructure.tools.ResearchToolConfig,
|
||||
): ToolConfig {
|
||||
val allowed = setOf(workspaceRoot, workingDir)
|
||||
return ToolConfig(
|
||||
@@ -648,6 +663,7 @@ private fun buildToolConfig(
|
||||
allowedPaths = allowed,
|
||||
workingDir = workingDir,
|
||||
),
|
||||
research = research,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -655,7 +671,9 @@ private fun buildToolConfigForWorkspace(
|
||||
workspace: WorkspaceContext,
|
||||
shellAllowedExecutables: Set<String>,
|
||||
toolsConfig: com.correx.core.config.ToolsConfig,
|
||||
research: com.correx.infrastructure.tools.ResearchToolConfig,
|
||||
): ToolConfig = ToolConfig(
|
||||
research = research,
|
||||
shell = ShellConfig(
|
||||
enabled = toolsConfig.shellEnabled,
|
||||
allowedExecutables = shellAllowedExecutables,
|
||||
|
||||
Reference in New Issue
Block a user