Files
agentik/agent-toolsets
subochev ac5d209fce
ci / JVM build + tests (push) Failing after 2m5s
refactor(standalone): extract modules, event-driven background, AppConfig
Standalone refactor — modularity + correctness improvements after
STANDALONE-REVIEW findings. Touches ~30 files. Build green, 178 tests pass.

(1) Module extractions — generic components out of :standalone:

  • :llm-tools (new KMP module, package pw.binom.agentik.llm.tools)
    - LlmReflector, SkillMiner, LlmMemoryReviewer, LiteLlmContextCompactor
    - Parsers: ReflectionParser, SkillMiningParser, ReviewDecisionParser
    - Prompts: ReflectionPrompts, SkillMiningPrompts, ReviewPrompts

  • :mcp-bridge (new JVM module, package pw.binom.agentik.mcp.bridge)
    - McpConfig, McpRegistry, McpLiteToolAdapter

  • NamedTool moved from :standalone to :agent-toolsets/commonMain
    - Generic (name + LiteTool) wrapper, used by both :mcp-bridge
      and :standalone's tool dispatcher

  :standalone loses ~1400 lines, depends on the two new modules.

(2) Background work → event-driven (no more interval-polling):

  • New :standalone/agent/BackgroundEvents.kt — internal event bus:
    - ToolCallEvent.Succeeded/Failed (emitted by ToolDispatcher after invoke)
    - CompactionEvent.Triggered (emitted by CompactionCoordinator pre-delete)
    - ConversationLifecycleEvent.Closing (emitted by ConversationLoop.close)

  • BackgroundScheduler rewritten as event subscriber:
    - On Closing: final reflection + skill mining (last-chance extraction)
    - On Compaction (turnsToDelete > 10): skill mining (debounced 60s)
    - On ToolFailure x2 in 60s window: reflection (debounced 5min)
    - Dropped: maybeScheduleReview/Reflection/SkillMining (interval-based)
    - Dropped config: memoryReviewInterval, reflectionInterval, skillMiningInterval

  • ToolDispatcher emits ToolCallEvent after each invoke.
  • CompactionCoordinator emits CompactionEvent before workingMemory.compact().
  • ConversationLoop.close() emits Closing BEFORE agentScope.cancel() so the
    subscription gets to run final reflection/mining.

  Net effect: typical 30-turn conversation runs ~38 LLM calls (was: 30 main +
  3 review + 3 reflection + 2 mining). With event-driven, review/mining only fire
  when their triggers actually make sense (compaction about to delete, or
  conversation closing).

(3) AppConfig single source of truth:

  • Replaces AgentikConfig + LlmConfig.fromEnv + McpConfig.fromEnv with one
    AppConfig.fromEnv() that reads all ~25 env vars in a single pass.
  • Sections: AgentSection, LlmSection, McpSection, MemorySection,
    EmbeddingSection, ReflectionSection, SkillMiningSection, DebugSection.
  • OPENAI_CONTEXT_WINDOW / AGENTIK_GOOGLE_CONTEXT_WINDOW no longer
    read twice (was a bug per STANDALONE-REVIEW E3).

(4) Other fixes inherited from earlier waves:

  • Hardening — size caps on user-input boundaries:
    MAX_MEMORY_CONTENT_LEN=32KB, MAX_SKILL_BODY_LEN=64KB,
    MAX_MCP_CONFIG_BYTES=1MB, MAX_A2A_REPLY_LEN=10MB, MAX_PORT=65535,
    blank-rejection in LlmConfig.requireEnv, URL/command validation.
  • Single scope — :standalone/agent/ConversationLoop has one
    agentScope (was: scope + backgroundScope).
  • liteConvRef race fix — capture-then-use pattern replaces !!-after-read;
    close() + runTurn.finally race on LiteConv JNI handled via
    AtomicReference.getAndSet.
  • SkillMiner.maxTurns / LlmReflector.maxTurns exposed as public (needed
    by BackgroundScheduler for prompt sizing).
  • Tests: MemoryWiringTest updated for new compaction-triggered review
    behavior; all parser/test imports updated for new packages.

Test results: 178/178 in :standalone, 36/36 in :agent-toolsets — all green.
2026-09-18 20:43:54 +03:00
..

:agent-toolsets — реестр инструментов агента (KMP, jvm + native)

Что это

Ядро системы tools для LLM-агента:

  • Toolset — интерфейс, объединяющий несколько связанных tools (MemoryTools, SkillsTools, FileSystemTools).
  • ToolRegistry — глобальный реестр + фильтр enabled/disabled.
  • ToolDispatcher — берёт решение LLM (вызов инструмента с аргументами) → запускает → возвращает результат.
  • Cooperative cancel — interrupt() корректно отменяет in-flight вызов, помечая результат [cancelled by user].
  • Concurrency budget — backgroundScope = Dispatchers.IO .limitedParallelism(4) (см. коммит 86eb063) — защищает threadpool от переполнения при fan-out 30+ диалогов.

Решает: надёжный механизм tool-calls с прерываниями, без blocking-pool exhaustion, без утечки. Переиспользуется во всех IM-фронтендах (CLI, TUI, IRC, web).

Где используется

  • :standalone подключает несколько Toolset-имплементаций (memory / skills / files / web), фильтрует через AGENTIK_TOOLSETS_DEFAULT env.

Как подключить

commonMain.dependencies {
    api("pw.binom.agentik:agent-toolsets:0.1.0")
}

class MyToolset : Toolset {
    override val name = "my"
    override val description = "Custom user-defined tools"
    override val tools = listOf(myTool1, myTool2)
}

val dispatcher = ToolDispatcher(
    toolsets = listOf(MemoryTools(memory), MyToolset()),
    enabled = setOf("memory", "my"),
)

Версии

gradle/libs.versions.toml → [versions] agentik-agent-toolsets.

Как пишется tool

data object EchoTool : Tool {
    override val name = "echo"
    override val description = "Echoes back the argument"
    override val argsSchema = jsonSchema {
        property("text", JsonType.STRING) { required = true }
    }

    override suspend fun invoke(args: JsonObject): ToolResult {
        val text = args["text"]?.jsonPrimitive?.content ?: return ToolResult.Error("missing text")
        return ToolResult.Text(text)
    }
}

Тесты

./gradlew :agent-toolsets:allTests

Покрывают: invoke happy-path, invalid args, cooperative cancel, budget exhaustion, registry filter, parallel dispatch.

Чего здесь НЕТ

  • Никакого конкретного LLM. Dispatcher вызывает tools, не LLM.
  • Никакого persistent storage. Опирается на контракт WorkingMemoryStore (см. :storage-core).

Текущий статус

Используется продакшеном. Реализует полную спецификацию из INTERRUPT-DESIGN.md: tool exchange log, rolling buffer, partial-state persistence.