Agent Loop Conventions
Overview
KB uses three loop patterns:
runIntentLoop— the primary harness for the public KB intents: one read intent (query) and two mutation intents (submit,invalidate).- Domain-specific cycle loops — deterministic multi-pass orchestration for commands with a fixed lifecycle such as
kb initandkb publish. agentLoop— low-level async generator for autonomous tool-calling. Available for programmatic / SDK use; not used by the CLI.
The public KB intents delegate their core logic to orchestrators:
| Intent | Orchestrator | Location |
|---|---|---|
query_truth |
Router-owned retrieval path | src/intents/router.ts |
submit_fact |
SubmitOrchestrator |
src/tools/submit-orchestrator.ts |
invalidate_fact |
InvalidateOrchestrator |
src/tools/invalidate-orchestrator.ts |
This is the composition principle: intent → orchestrator → tools. runIntentLoop owns retry policy; orchestrators own multi-step behavior; CLI/TUI adapters stay thin.
Intent Surface
flowchart LR
Q["kb query / /query"] --> R["read_facts\nfact FTS + deep facts loop"]
R --> G["graph query expansion\n+ typed edge hints"]
G --> A["grounded answer"]
S["kb submit / /submit"] --> SO["SubmitOrchestrator"]
SO --> W["discover target + upsert fact"]
W --> SG["extract + upsert graph"]
I["kb invalidate / /invalidate"] --> IO["InvalidateOrchestrator"]
IO --> P["preview/apply KB mutation"]
P --> IG["soft-delete graph provenance"]
Part 1: Intent Loop
File: src/core/intent-loop.ts
runIntentLoop is the entry point for the full public KB intent surface: one read intent plus two mutation intents.
Signature
runIntentLoop(
envelope: ConsumerIntentEnvelope,
toolExecutor: ToolExecutor,
config?: IntentLoopConfig,
): Promise<IntentLoopResult>
interface IntentLoopConfig {
maxIterations?: number
confidenceThreshold?: number
provider?: LLMProvider
}
interface IntentLoopResult {
result: IntentResult
iterations: number
escalated: boolean
}
Per-intent behaviour
| Intent | Retry? | Strategy |
|---|---|---|
query_truth |
Yes, up to maxIterations |
Router defaults to deep discovery; weak retrieval escalates to deep with a wider limit. |
submit_fact |
No | Single pass — mutation flow is orchestrator-owned and graph sync happens inside SubmitOrchestrator. |
invalidate_fact |
No | Single pass — preview/apply and graph invalidation are orchestrator-owned. |
Weak retrieval for query means zero results, fewer than two results, or a final retrieval checkpoint with status: 'miss' or 'error'.
Query sequence
sequenceDiagram
autonumber
participant U as CLI / TUI
participant L as runIntentLoop
participant R as DefaultIntentRouter
participant D as read_facts
participant G as Graph augmentation
U->>L: query_truth envelope
L->>R: execute(query_truth)
R->>D: read_facts(query, discoveryDepth, limit)
D->>G: query expansion / hints when graph enabled
G-->>R: grounded retrieval results
R-->>L: IntentResult
L-->>U: answer-ready result
CLI wiring
All KB intents go through the same loop. Query-specific answer enrichment runs afterward only for the read intent.
Part 2: Domain-Specific Cycle Loops
Some commands implement deterministic loops over named cycles. LLM is called directly via provider.call() because the command owns the orchestration.
kb init
| Cycle | What happens | Output |
|---|---|---|
read-inputs |
Scan source files, ask user interview questions | InitContext |
pass1 |
One LLM call per coverage topic in parallel | CandidateDoc[] |
pass2 |
Coverage gap analysis, follow-up questions, refinement | Updated CandidateDoc[] |
pass-enrich |
Per-document enrichment in parallel | Enriched CandidateDoc[] |
pass3 |
Final quality pass | Final CandidateDoc[] |
write |
Upsert to SQLite | Written document IDs |
pass-graph |
Extract entities and relationships into SQLite (kb_graph_*) |
Graph store on disk |
Part 3: Choosing a Pattern
| Situation | Use |
|---|---|
Public KB intent (query, submit, invalidate) |
runIntentLoop |
| Fixed sequence of LLM passes with known inputs/outputs | Cycle loop |
| User interaction between LLM calls | Cycle loop |
| Autonomous open-ended tool use in SDK/programmatic context | agentLoop |
| Single LLM completion, no tools | provider.call() directly |
See Also
src/core/intent-loop.ts— primary intent harnesssrc/cli/intent-cli.ts— public intent parsing and formattingsrc/tools/submit-orchestrator.ts— KB write + graph syncsrc/tools/invalidate-orchestrator.ts— KB invalidation + graph cleanupsrc/tools/GRAPH.md— graph lifecycle and read/write semantics