@kb-labs/agent-core 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2415 @@
1
+ import * as _kb_labs_agent_contracts from '@kb-labs/agent-contracts';
2
+ import { AgentMode, AgentSessionInfo, AgentSession, AgentEvent, Turn, FileChangeSummary, AgentEventCallback, Tracer, LLMTier, TaskPlan, SessionProgress, TaskSpec, AgentConfig, TaskResult, ModeConfig, SpecSection, StopConditionResult, LoopResult, AgentMemory, FeatureFlags, SpawnAgentRequest, SpawnAgentResult, AsyncTask, ToolPack, ToolFilter, ToolDefinition, ResolvedTool, ToolResult, IterationSnapshot, RunEvaluation } from '@kb-labs/agent-contracts';
3
+ import { ToolRegistry, SessionMemoryBridge } from '@kb-labs/agent-tools';
4
+ import { RuntimeProfile, ResultMapper, StopCondition, RunContext, LLMCallResult, ExecutionLoop as ExecutionLoop$1, LoopContext as LoopContext$1, LoopResult as LoopResult$1, AgentMiddleware, ControlAction, LLMCtx, LLMCallPatch, ToolExecCtx, ToolOutput, IAgentRunner, AgentSDK, ToolGuard, OutputProcessor, InputNormalizer, ToolCallInput, ContextMeta } from '@kb-labs/agent-sdk';
5
+ export { AgentMiddleware, ControlAction, LLMCallPatch, LLMCallResult, LLMCtx, RunContext, ToolExecCtx, ToolOutput } from '@kb-labs/agent-sdk';
6
+ import { StorageConfig, ChangeStore, FileChangeSummary as FileChangeSummary$1 } from '@kb-labs/agent-history';
7
+ import { LLMMessage, ILLM, LLMTool } from '@kb-labs/sdk';
8
+ export { SessionArtifactStore } from '@kb-labs/agent-store';
9
+ export { completePendingActions, createKernelState, ingestUserTurn, isCorrectionPendingAction, projectKernelPrompt, recordCorrection, recordDecision, recordToolArtifact } from '@kb-labs/agent-kernel';
10
+
11
+ /**
12
+ * Extended session with agentId for storage
13
+ */
14
+ interface StoredSession extends AgentSession {
15
+ agentId: string;
16
+ name?: string;
17
+ }
18
+ /**
19
+ * Conversation turn extracted from events
20
+ */
21
+ interface ConversationTurn {
22
+ userTask: string;
23
+ agentResponse?: string;
24
+ timestamp: string;
25
+ }
26
+ interface SessionKpiBaseline {
27
+ version: number;
28
+ updatedAt: string;
29
+ driftRateEma: number;
30
+ evidenceDensityEma: number;
31
+ toolErrorRateEma: number;
32
+ samples: number;
33
+ tokenHistory: number[];
34
+ iterationUtilizationHistory: number[];
35
+ qualityScoreHistory: number[];
36
+ }
37
+ /**
38
+ * Manages agent sessions (creation, persistence, retrieval)
39
+ */
40
+ declare class SessionManager {
41
+ private workingDir;
42
+ private readonly artifactStore;
43
+ /** In-memory cache of per-run sequence counters (initialized lazily from NDJSON) */
44
+ private runSeqCounters;
45
+ /** In-memory cache of turn sequence counters per session (for turns.json). */
46
+ private turnSeqCounters;
47
+ /** Turn assembler for creating turn snapshots from events */
48
+ private assembler;
49
+ /** Write queue per session to prevent concurrent write race conditions */
50
+ private writeQueues;
51
+ /**
52
+ * Serialized event-processing queue per session.
53
+ * Ensures addEvent calls for the same session are processed in arrival order
54
+ * even when the caller fires them concurrently with void (fire-and-forget).
55
+ * This prevents tool:end being processed by TurnAssembler before tool:start.
56
+ */
57
+ private eventQueues;
58
+ /** Write queue per session for artifact projection updates */
59
+ private artifactWriteQueues;
60
+ /** Write queue per session for KPI baseline updates */
61
+ private kpiWriteQueues;
62
+ constructor(workingDir: string);
63
+ /**
64
+ * Create a new session
65
+ */
66
+ createSession(config: {
67
+ mode: AgentMode;
68
+ task: string;
69
+ agentId: string;
70
+ name?: string;
71
+ planId?: string;
72
+ sessionId?: string;
73
+ }): Promise<AgentSessionInfo>;
74
+ /**
75
+ * Generate a human-readable session name from task
76
+ */
77
+ private generateSessionName;
78
+ /**
79
+ * Convert stored session to session info
80
+ */
81
+ private toSessionInfo;
82
+ /**
83
+ * Save session to disk
84
+ */
85
+ saveSession(session: StoredSession | AgentSession): Promise<void>;
86
+ /**
87
+ * Load session from disk
88
+ */
89
+ loadSession(sessionId: string): Promise<StoredSession | null>;
90
+ /**
91
+ * Get session with full info
92
+ */
93
+ getSessionInfo(sessionId: string): Promise<AgentSessionInfo | null>;
94
+ /**
95
+ * Update session status
96
+ */
97
+ updateSessionStatus(sessionId: string, status: AgentSession['status']): Promise<void>;
98
+ /**
99
+ * List all sessions with info
100
+ */
101
+ listSessions(options?: {
102
+ agentId?: string;
103
+ status?: AgentSession['status'];
104
+ limit?: number;
105
+ offset?: number;
106
+ }): Promise<{
107
+ sessions: AgentSessionInfo[];
108
+ total: number;
109
+ }>;
110
+ /**
111
+ * Get next per-run sessionSeq for a specific run within a session.
112
+ * Each run has independent sequence counters starting from 1.
113
+ * Lazily initialized: reads NDJSON on first call to find max existing sessionSeq for this runId,
114
+ * then uses in-memory counter for subsequent calls.
115
+ */
116
+ private getNextSessionSeq;
117
+ /**
118
+ * Add event to session (append-only, race-condition safe).
119
+ * Assigns per-run sessionSeq for ordering within each run.
120
+ * Also processes event to update turn snapshots.
121
+ *
122
+ * Events for the same session are serialized via eventQueues so that
123
+ * concurrent fire-and-forget callers (void addEvent(...)) don't cause
124
+ * tool:end to be processed before tool:start in TurnAssembler.
125
+ */
126
+ addEvent(sessionId: string, event: AgentEvent): Promise<void>;
127
+ private _addEventInternal;
128
+ /**
129
+ * Get all events for a session
130
+ */
131
+ getSessionEvents(sessionId: string, options?: {
132
+ limit?: number;
133
+ offset?: number;
134
+ types?: string[];
135
+ }): Promise<AgentEvent[]>;
136
+ /**
137
+ * Count events in session
138
+ */
139
+ countEvents(sessionId: string, types?: string[]): Promise<number>;
140
+ /**
141
+ * Extract conversation history from session events
142
+ *
143
+ * Parses events.ndjson to reconstruct user-agent conversation turns.
144
+ * Uses agent:start events (without parentAgentId = top-level requests).
145
+ * Uses agent:end for summaries.
146
+ *
147
+ * @param sessionId - Session ID
148
+ * @param maxTurns - Maximum number of turns to return (default: 10)
149
+ * @returns Array of conversation turns, oldest first
150
+ */
151
+ getConversationHistory(sessionId: string, maxTurns?: number): Promise<Array<{
152
+ userTask: string;
153
+ agentResponse?: string;
154
+ timestamp: string;
155
+ }>>;
156
+ /**
157
+ * Build conversation history from canonical turn snapshots (turns.json).
158
+ * This avoids lossy reconstruction from events summary.
159
+ */
160
+ private getConversationHistoryFromTurns;
161
+ private extractAssistantText;
162
+ /**
163
+ * Get conversation history with progressive summarization for long sessions
164
+ *
165
+ * Strategy:
166
+ * - Recent turns (last 3): Full context (up to 2000 chars each)
167
+ * - Mid-term turns (4-10): Summarized (up to 500 chars each)
168
+ * - Old turns (11+): Ultra-brief (up to 150 chars each, max 10 oldest)
169
+ *
170
+ * This keeps token usage bounded while maintaining context depth.
171
+ *
172
+ * @param sessionId - Session ID
173
+ * @param llm - Optional LLM instance for intelligent summarization (if not provided, uses truncation)
174
+ * @returns Structured history with recent/mid-term/old tiers
175
+ */
176
+ getConversationHistoryWithSummarization(sessionId: string, llm?: {
177
+ chat?: (messages: Array<{
178
+ role: string;
179
+ content: string;
180
+ }>) => Promise<string>;
181
+ }): Promise<{
182
+ recent: ConversationTurn[];
183
+ midTerm: ConversationTurn[];
184
+ old: ConversationTurn[];
185
+ }>;
186
+ /**
187
+ * Store turn snapshot to turns.json
188
+ * Serializes writes per session to prevent race-condition duplicates
189
+ */
190
+ storeTurnSnapshot(sessionId: string, turn: Turn): Promise<void>;
191
+ private _writeTurnSnapshot;
192
+ /**
193
+ * Attach file change summaries to the assistant turn for a given runId.
194
+ * Called after agent.execute() completes to surface file changes in the UI.
195
+ */
196
+ attachFileChangesToTurn(sessionId: string, runId: string, fileChanges: FileChangeSummary[]): Promise<void>;
197
+ private _attachFileChangesToTurn;
198
+ /**
199
+ * Update fileChanges metadata on a turn (e.g. after approve/rollback).
200
+ * Patches individual change entries by changeId.
201
+ */
202
+ updateTurnFileChanges(sessionId: string, runId: string, updatedChanges: _kb_labs_agent_contracts.FileChangeSummary[]): Promise<void>;
203
+ /**
204
+ * Load turn snapshots for session
205
+ */
206
+ getTurns(sessionId: string): Promise<Turn[]>;
207
+ /**
208
+ * Lazy migration: rebuild turns from events.ndjson
209
+ * (for old sessions without turns.json)
210
+ */
211
+ private migrateTurnsFromEvents;
212
+ /**
213
+ * Get conversation snapshot for WebSocket connections
214
+ * Returns last 20 completed turns + active turns
215
+ */
216
+ getConversationSnapshot(sessionId: string): Promise<{
217
+ completedTurns: Turn[];
218
+ activeTurns: Turn[];
219
+ totalTurns: number;
220
+ }>;
221
+ /**
222
+ * Create user turn with task/question
223
+ * Called when user submits a task to agent
224
+ */
225
+ createUserTurn(sessionId: string, task: string, runId: string): Promise<Turn>;
226
+ /**
227
+ * Reserve next turn sequence number atomically per session.
228
+ * Uses the same serialized write queue as turns.json updates.
229
+ */
230
+ private reserveNextTurnSequence;
231
+ /**
232
+ * Process event and update turn snapshot
233
+ * Returns updated turn if changed, null otherwise
234
+ */
235
+ processEventAndUpdateTurn(sessionId: string, event: AgentEvent): Promise<Turn | null>;
236
+ /**
237
+ * Store extracted trace artifacts for a completed run.
238
+ */
239
+ storeTraceArtifacts(sessionId: string, runId: string, traceEntries: Array<Record<string, unknown>>): Promise<void>;
240
+ /**
241
+ * Render compact trace-memory context for prompt injection.
242
+ */
243
+ getTraceArtifactsContext(sessionId: string): Promise<string>;
244
+ private loadArtifacts;
245
+ private updateArtifacts;
246
+ private extractTraceArtifacts;
247
+ private mergeArtifacts;
248
+ private uniqueTail;
249
+ private createEmptyArtifacts;
250
+ /**
251
+ * Get session directory path
252
+ */
253
+ getSessionDir(sessionId: string): string;
254
+ /**
255
+ * Get events file path (NDJSON format)
256
+ */
257
+ getEventsPath(sessionId: string): string;
258
+ /**
259
+ * Get session plan file path
260
+ */
261
+ getSessionPlanPath(sessionId: string): string;
262
+ /**
263
+ * Get session spec JSON file path
264
+ */
265
+ getSessionSpecPath(sessionId: string): string;
266
+ /**
267
+ * Get session spec markdown file path
268
+ */
269
+ getSessionSpecMdPath(sessionId: string): string;
270
+ /**
271
+ * Get session progress file path
272
+ */
273
+ getSessionProgressPath(sessionId: string): string;
274
+ /**
275
+ * Get turns file path (Turn snapshots JSON)
276
+ */
277
+ getTurnsPath(sessionId: string): string;
278
+ /**
279
+ * Get trace artifacts projection path
280
+ */
281
+ getArtifactsPath(sessionId: string): string;
282
+ /**
283
+ * Get KPI baseline projection path
284
+ */
285
+ getKpiBaselinePath(sessionId: string): string;
286
+ /**
287
+ * Read persisted KPI baseline for this session.
288
+ */
289
+ getKpiBaseline(sessionId: string): Promise<SessionKpiBaseline | null>;
290
+ /**
291
+ * Update persisted KPI baseline with serialized writes per session.
292
+ */
293
+ updateKpiBaseline(sessionId: string, updater: (current: SessionKpiBaseline | null) => SessionKpiBaseline): Promise<void>;
294
+ }
295
+
296
+ /**
297
+ * Plan generator - creates execution plans using LLM with native tool calling
298
+ */
299
+
300
+ /**
301
+ * Generates execution plans from task descriptions
302
+ */
303
+ declare class PlanGenerator {
304
+ /**
305
+ * Generate a task execution plan using native tool calling
306
+ */
307
+ generate(config: {
308
+ task: string;
309
+ sessionId: string;
310
+ mode: AgentMode;
311
+ complexity?: 'simple' | 'medium' | 'complex';
312
+ toolRegistry?: ToolRegistry;
313
+ researchContext?: string;
314
+ onEvent?: AgentEventCallback;
315
+ agentId?: string;
316
+ parentAgentId?: string;
317
+ tracer?: Tracer;
318
+ tier?: LLMTier;
319
+ }): Promise<TaskPlan>;
320
+ /**
321
+ * Update an existing plan based on user feedback.
322
+ * Uses a dedicated plan_update tool instead of free-form JSON.
323
+ */
324
+ update(config: {
325
+ plan: TaskPlan;
326
+ feedback: string;
327
+ mode?: AgentMode;
328
+ toolRegistry?: ToolRegistry;
329
+ enableResearch?: boolean;
330
+ onEvent?: AgentEventCallback;
331
+ agentId?: string;
332
+ parentAgentId?: string;
333
+ tracer?: Tracer;
334
+ tier?: LLMTier;
335
+ }): Promise<TaskPlan>;
336
+ private buildTaskPlan;
337
+ private runResearchPhase;
338
+ private collectResearchContext;
339
+ private collectDeterministicResearchEvidence;
340
+ private runDelegatedResearchPhase;
341
+ private selectResearchGaps;
342
+ private normalizeChildResearchOutput;
343
+ private regeneratePlanWithQualityFeedback;
344
+ private emitStatus;
345
+ private emitProgress;
346
+ private emitEvent;
347
+ private getTokensUsed;
348
+ private traceLLMCall;
349
+ private newToolCallId;
350
+ private assessPlanQuality;
351
+ private assessFreeformMarkdown;
352
+ private isRefactorTask;
353
+ private shouldRetryPlan;
354
+ private clamp01;
355
+ private createResearchTool;
356
+ /**
357
+ * Create the plan generation tool
358
+ */
359
+ private createPlanTool;
360
+ private createRefactorPlanTool;
361
+ private createPlanUpdateTool;
362
+ /**
363
+ * Get research tools from registry (read-only tools for investigation)
364
+ */
365
+ private getResearchTools;
366
+ /**
367
+ * Normalize phases from tool call
368
+ */
369
+ private normalizePhasesFromTool;
370
+ private parsePhasesFromMarkdown;
371
+ private normalizePlanMarkdown;
372
+ private createFallbackPhases;
373
+ private synthesizeRefactorPhasesFromChangeSets;
374
+ private normalizePhaseName;
375
+ private normalizePhaseDescription;
376
+ private normalizeStepAction;
377
+ private normalizeExpectedOutcome;
378
+ private extractHumanLabel;
379
+ private extractOutcomeFromAction;
380
+ /**
381
+ * Build system prompt for plan generation
382
+ */
383
+ private buildSystemPrompt;
384
+ private buildResearchSystemPrompt;
385
+ private buildUpdateSystemPrompt;
386
+ private buildPlanningProfile;
387
+ private extractPathHints;
388
+ private extractKeywordHints;
389
+ /**
390
+ * Get mode-specific context for plan generation
391
+ */
392
+ private getModeContext;
393
+ private getPlanToolName;
394
+ }
395
+
396
+ /**
397
+ * Plan executor - executes plans step by step with progress tracking
398
+ */
399
+
400
+ /**
401
+ * Executes task plans with progress tracking
402
+ */
403
+ declare class PlanExecutor {
404
+ private tracer?;
405
+ constructor(tracer?: Tracer);
406
+ /**
407
+ * Execute a plan
408
+ */
409
+ execute(plan: TaskPlan, toolExecutor: (tool: string, args: Record<string, unknown>) => Promise<string>): Promise<{
410
+ success: boolean;
411
+ completedPhases: number;
412
+ completedSteps: number;
413
+ failedSteps: number;
414
+ error?: string;
415
+ }>;
416
+ /**
417
+ * Check if phase dependencies are met
418
+ */
419
+ private areDependenciesMet;
420
+ /**
421
+ * Calculate progress
422
+ */
423
+ calculateProgress(plan: TaskPlan): SessionProgress;
424
+ /**
425
+ * Save progress to file
426
+ */
427
+ saveProgress(progress: SessionProgress, filePath: string): Promise<void>;
428
+ /**
429
+ * Record trace entry
430
+ */
431
+ private recordTrace;
432
+ }
433
+
434
+ interface PlanDocumentServiceOptions {
435
+ plansDir?: string;
436
+ }
437
+ declare class PlanDocumentService {
438
+ private readonly workingDir;
439
+ private readonly plansDir;
440
+ constructor(workingDir: string, options?: PlanDocumentServiceOptions);
441
+ getPlansDir(): string;
442
+ getPlanPath(plan: TaskPlan): string;
443
+ createDraft(plan: TaskPlan): Promise<{
444
+ path: string;
445
+ markdown: string;
446
+ }>;
447
+ renderDraft(plan: TaskPlan): string;
448
+ reviseDraft(planPath: string, markdown: string): Promise<string>;
449
+ appendExecutionLog(planPath: string, entry: string): Promise<string>;
450
+ refreshToc(markdown: string): string;
451
+ private buildDraftMarkdown;
452
+ private expandPhaseKeySteps;
453
+ private collectPhaseExpectedResults;
454
+ private collectVerificationCommands;
455
+ private compactSentence;
456
+ private removeExistingToc;
457
+ private collectHeadings;
458
+ private renderToc;
459
+ private slugify;
460
+ private slugifyHeading;
461
+ }
462
+
463
+ /**
464
+ * Plan Output Validator — deterministic rubric-based quality gate for generated plans.
465
+ *
466
+ * Checks specificity, actionability, completeness, and verification without LLM calls.
467
+ * Returns a composite score (0–1) and pass/fail verdict.
468
+ */
469
+ type Severity = 'error' | 'warning' | 'info';
470
+ interface ValidatorIssue {
471
+ dimension: 'specificity' | 'actionability' | 'completeness' | 'verification';
472
+ severity: Severity;
473
+ message: string;
474
+ }
475
+ interface RubricScore {
476
+ raw: number;
477
+ weighted: number;
478
+ details: string;
479
+ }
480
+ interface PlanValidationResult {
481
+ passed: boolean;
482
+ score: number;
483
+ rubric: {
484
+ specificity: RubricScore;
485
+ actionability: RubricScore;
486
+ completeness: RubricScore;
487
+ verification: RubricScore;
488
+ };
489
+ issues: ValidatorIssue[];
490
+ }
491
+ declare class PlanValidator {
492
+ /**
493
+ * Validate a markdown plan and return rubric-based scoring.
494
+ */
495
+ validate(markdown: string): PlanValidationResult;
496
+ private scoreSpecificity;
497
+ private scoreActionability;
498
+ private scoreCompleteness;
499
+ private scoreVerification;
500
+ }
501
+
502
+ /**
503
+ * Spec Output Validator — deterministic rubric-based quality gate for generated specs.
504
+ *
505
+ * Checks coverage (plan steps covered), precision (before/after diffs present),
506
+ * and file validity without LLM calls.
507
+ * Returns a composite score (0–1) and pass/fail verdict.
508
+ */
509
+
510
+ type SpecSeverity = 'error' | 'warning' | 'info';
511
+ interface SpecValidatorIssue {
512
+ dimension: 'coverage' | 'precision' | 'files';
513
+ severity: SpecSeverity;
514
+ message: string;
515
+ }
516
+ interface SpecRubricScore {
517
+ raw: number;
518
+ weighted: number;
519
+ details: string;
520
+ }
521
+ interface SpecValidationResult {
522
+ passed: boolean;
523
+ score: number;
524
+ metrics: {
525
+ planStepCount: number;
526
+ coveredStepRefs: number;
527
+ uncoveredStepCount: number;
528
+ phaseCoverage: number;
529
+ stepCoverage: number;
530
+ diffPairs: number;
531
+ };
532
+ rubric: {
533
+ coverage: SpecRubricScore;
534
+ precision: SpecRubricScore;
535
+ files: SpecRubricScore;
536
+ };
537
+ issues: SpecValidatorIssue[];
538
+ }
539
+ declare class SpecValidator {
540
+ /**
541
+ * Validate a spec against its source plan and return rubric-based scoring.
542
+ */
543
+ validate(spec: TaskSpec, plan: TaskPlan): SpecValidationResult;
544
+ /**
545
+ * Validate from raw markdown (when structured spec is not yet parsed).
546
+ */
547
+ validateMarkdown(markdown: string, plan: TaskPlan): SpecValidationResult;
548
+ private scoreCoverage;
549
+ private scorePrecision;
550
+ private scoreFiles;
551
+ }
552
+
553
+ /**
554
+ * Mode handler interface and base implementation
555
+ */
556
+
557
+ /**
558
+ * Mode handler interface - each mode implements this
559
+ */
560
+ interface ModeHandler {
561
+ /**
562
+ * Execute task in this mode
563
+ */
564
+ execute(task: string, config: AgentConfig, toolRegistry: ToolRegistry): Promise<TaskResult>;
565
+ }
566
+ /**
567
+ * Get mode handler for specified mode.
568
+ * Delegates to ModeRegistry — supports built-in and custom modes.
569
+ */
570
+ declare function getModeHandler(modeConfig?: ModeConfig): Promise<ModeHandler>;
571
+
572
+ /**
573
+ * Execute mode handler - standard task execution with optional revision loop.
574
+ *
575
+ * When a session has an approved plan, the plan markdown is injected into
576
+ * the task prompt and the plan status is tracked through its lifecycle:
577
+ * approved → in_progress → completed | failed
578
+ *
579
+ * Revision loop (when enabled):
580
+ * execute → quality gate → if fail → compact summary → inject feedback → re-execute
581
+ * Max 2 revisions. Quality gate is heuristic (no LLM).
582
+ */
583
+
584
+ /**
585
+ * Execute mode - standard agent execution
586
+ */
587
+ declare class ExecuteModeHandler implements ModeHandler {
588
+ execute(task: string, config: AgentConfig, toolRegistry: ToolRegistry): Promise<TaskResult>;
589
+ /**
590
+ * Single execution attempt.
591
+ */
592
+ private runOnce;
593
+ /**
594
+ * Build a revision task that includes compact feedback from previous attempt.
595
+ */
596
+ private buildRevisionTask;
597
+ /**
598
+ * Load approved (or draft) plan from session storage.
599
+ * Falls back to plan.md (written by plan_write tool) if plan.json not available.
600
+ */
601
+ private loadApprovedPlan;
602
+ /**
603
+ * Update plan status on disk.
604
+ */
605
+ private updatePlanStatus;
606
+ /**
607
+ * Inject full plan markdown into the task prompt so the agent follows it.
608
+ * Also adds a todo bootstrap instruction so the agent tracks progress.
609
+ */
610
+ private buildTaskWithPlan;
611
+ /**
612
+ * Build a todo_create instruction from plan phases so the agent tracks progress.
613
+ * Returns null if no phases or sessionId is missing.
614
+ */
615
+ private buildTodoBootstrap;
616
+ /**
617
+ * Fallback compact formatter when markdown is not stored on the plan object.
618
+ */
619
+ private formatPhasesCompact;
620
+ }
621
+
622
+ /**
623
+ * Plan mode handler - lightweight "Claude Code style" planning flow.
624
+ *
625
+ * Core behavior:
626
+ * - Reuse regular Agent loop (research + tool use).
627
+ * - Restrict tool access to read-only/discovery/reporting tools.
628
+ * - Ask agent to output a markdown plan (free-form, human-oriented).
629
+ * - Persist plan as session plan.json + canonical markdown document.
630
+ */
631
+
632
+ declare class PlanModeHandler implements ModeHandler {
633
+ execute(task: string, config: AgentConfig, toolRegistry: ToolRegistry): Promise<TaskResult>;
634
+ private loadPlan;
635
+ private buildPlanPrompt;
636
+ private createSubRunner;
637
+ private buildFallbackPlan;
638
+ private buildBudgetSnapshot;
639
+ private emit;
640
+ }
641
+
642
+ declare function createPlanRuntimeProfile(options?: {
643
+ workingDir?: string;
644
+ sessionManager?: SessionManager;
645
+ task?: string;
646
+ complexity?: 'simple' | 'medium' | 'complex';
647
+ existingPlan?: TaskPlan | null;
648
+ }): RuntimeProfile;
649
+
650
+ interface PlanOutputValidation {
651
+ passed: boolean;
652
+ summary: string;
653
+ result: PlanValidationResult;
654
+ }
655
+ declare class PlanOutputValidator {
656
+ private readonly validator;
657
+ validate(markdown: string): PlanOutputValidation;
658
+ private buildSummary;
659
+ }
660
+
661
+ interface PlanArtifactWriteResult {
662
+ planPath: string;
663
+ documentPath: string;
664
+ markdown: string;
665
+ }
666
+ declare class PlanArtifactWriter {
667
+ private readonly workingDir;
668
+ private readonly sessionManager;
669
+ private readonly documentService;
670
+ constructor(workingDir: string, sessionManager?: SessionManager, documentService?: PlanDocumentService);
671
+ write(sessionId: string, plan: TaskPlan): Promise<PlanArtifactWriteResult>;
672
+ }
673
+
674
+ interface PlanResultMapperOptions {
675
+ task: string;
676
+ complexity: 'simple' | 'medium' | 'complex';
677
+ existingPlan?: TaskPlan | null;
678
+ }
679
+ declare function createPlanResultMapper(options: PlanResultMapperOptions): ResultMapper;
680
+
681
+ /**
682
+ * Edit mode handler - modify existing files
683
+ */
684
+
685
+ /**
686
+ * Edit mode - focus on modifying existing files
687
+ */
688
+ declare class EditModeHandler implements ModeHandler {
689
+ execute(task: string, config: AgentConfig, toolRegistry: ToolRegistry): Promise<TaskResult>;
690
+ }
691
+
692
+ /**
693
+ * Debug mode handler - analyze errors with trace context
694
+ */
695
+
696
+ /**
697
+ * Debug mode - analyze errors and suggest fixes with trace context
698
+ */
699
+ declare class DebugModeHandler implements ModeHandler {
700
+ execute(task: string, config: AgentConfig, toolRegistry: ToolRegistry): Promise<TaskResult>;
701
+ }
702
+
703
+ /**
704
+ * Spec mode handler — generates a detailed specification from an approved plan.
705
+ *
706
+ * Not a separate AgentMode. Called from REST/CLI after plan approval.
707
+ * Creates a child agent with read-only tools + plan markdown in the system prompt.
708
+ * The agent reads actual source files, verifies code at specified line numbers,
709
+ * and produces exact before/after diffs for each plan step.
710
+ *
711
+ * Output: TaskSpec (structured) + spec.md (human-readable markdown).
712
+ */
713
+
714
+ declare class SpecModeHandler {
715
+ /**
716
+ * Generate a detailed specification from an approved plan.
717
+ *
718
+ * @param plan - Approved TaskPlan (must have status 'approved')
719
+ * @param config - Agent config (workingDir, onEvent, etc.)
720
+ * @param toolRegistry - Full tool registry (will be wrapped read-only)
721
+ * @returns TaskResult with .spec populated
722
+ */
723
+ execute(plan: TaskPlan, config: AgentConfig, toolRegistry: ToolRegistry): Promise<TaskResult>;
724
+ private buildSpecPrompt;
725
+ private runDelegatedResearchPacks;
726
+ private buildResearchPacks;
727
+ private chunkArray;
728
+ private collectUncoveredPlanSteps;
729
+ private tryRepairSpecWithCoverageGaps;
730
+ private resolveSpecBudgetPolicy;
731
+ private resolveSpecIterations;
732
+ private estimatePlanTokens;
733
+ private clampNumber;
734
+ private clampInt;
735
+ private buildFallbackPartialSpec;
736
+ private planToMarkdownFallback;
737
+ parseSpecSections(markdown: string, plan: TaskPlan): SpecSection[];
738
+ private parseChangeBlock;
739
+ private createSubRunner;
740
+ private emit;
741
+ private buildBudgetSnapshot;
742
+ }
743
+
744
+ /**
745
+ * ModeRegistry — dynamic mode registration without modifying core.
746
+ *
747
+ * Replaces the switch/case in getModeHandler() with a registry that can
748
+ * be extended with custom modes at runtime.
749
+ *
750
+ * Built-in modes are registered lazily (dynamic import) to preserve
751
+ * tree-shaking and the existing performance characteristics.
752
+ *
753
+ * Usage (custom mode):
754
+ * modeRegistry.register('review', () => new ReviewModeHandler());
755
+ * // Now agent config { mode: 'review' } uses ReviewModeHandler
756
+ *
757
+ * Usage (override built-in):
758
+ * modeRegistry.register('plan', () => new CustomPlanHandler(), { override: true });
759
+ */
760
+
761
+ /** Factory function that creates a ModeHandler instance */
762
+ type ModeHandlerFactory = () => ModeHandler | Promise<ModeHandler>;
763
+ interface ModeRegistration {
764
+ readonly mode: string;
765
+ readonly factory: ModeHandlerFactory;
766
+ readonly builtIn: boolean;
767
+ }
768
+ interface RegisterOptions {
769
+ /** Allow overriding an already-registered mode (including built-ins) */
770
+ override?: boolean;
771
+ }
772
+ declare class ModeRegistry {
773
+ private readonly registrations;
774
+ constructor();
775
+ /**
776
+ * Register a mode handler factory.
777
+ *
778
+ * @throws If mode is already registered and override is not set.
779
+ */
780
+ register(mode: string, factory: ModeHandlerFactory, options?: RegisterOptions): void;
781
+ /**
782
+ * Check whether a mode is registered.
783
+ */
784
+ has(mode: string): boolean;
785
+ /**
786
+ * List all registered mode names.
787
+ */
788
+ list(): string[];
789
+ /**
790
+ * Get a ModeHandler for the given mode.
791
+ * Falls back to 'execute' if mode is not found.
792
+ *
793
+ * @throws If neither the requested mode nor 'execute' fallback is registered.
794
+ */
795
+ get(mode: string): Promise<ModeHandler>;
796
+ private registerBuiltIns;
797
+ }
798
+ /**
799
+ * Global ModeRegistry instance.
800
+ *
801
+ * Use this to register custom modes application-wide:
802
+ * import { modeRegistry } from '@kb-labs/agent-core';
803
+ * modeRegistry.register('my-mode', () => new MyModeHandler());
804
+ */
805
+ declare const modeRegistry: ModeRegistry;
806
+ /**
807
+ * Convenience function that replaces the old getModeHandler() switch/case.
808
+ * Delegates to the global modeRegistry.
809
+ */
810
+ declare function getModeHandlerFromRegistry(mode: string): Promise<ModeHandler>;
811
+
812
+ type ExecutionPhase = 'init' | 'scoping' | 'planning_lite' | 'executing' | 'converging' | 'verifying' | 'reporting' | 'completed' | 'failed';
813
+ type PhaseTransition = {
814
+ from: ExecutionPhase;
815
+ to: ExecutionPhase;
816
+ timestamp: string;
817
+ reason?: string;
818
+ };
819
+ declare class ExecutionStateMachine {
820
+ private current;
821
+ private transitions;
822
+ private enteredAt;
823
+ private durationsMs;
824
+ getCurrent(): ExecutionPhase;
825
+ getTransitions(): PhaseTransition[];
826
+ transition(to: ExecutionPhase, reason?: string): void;
827
+ getPhaseDurationsMs(now?: number): Record<ExecutionPhase, number>;
828
+ }
829
+
830
+ type StepStatus = 'pending' | 'done' | 'failed';
831
+ type CapabilityType = 'discover_resource' | 'read_resource' | 'mutate_resource' | 'execute_command' | 'memory_access' | 'progress_tracking' | 'finalize_result' | 'integrate_external' | 'general_action';
832
+ type LedgerStep = {
833
+ id: string;
834
+ goal: string;
835
+ capability: CapabilityType;
836
+ toolName?: string;
837
+ status: StepStatus;
838
+ startedAt: string;
839
+ completedAt?: string;
840
+ durationMs?: number;
841
+ error?: string;
842
+ evidence?: string;
843
+ };
844
+ declare class TaskLedger {
845
+ private steps;
846
+ private activeById;
847
+ private counter;
848
+ startStep(params: {
849
+ goal: string;
850
+ capability: CapabilityType;
851
+ toolName?: string;
852
+ }): string;
853
+ completeStep(stepId: string, evidence?: string): void;
854
+ failStep(stepId: string, error: string): void;
855
+ getSummary(): {
856
+ totalSteps: number;
857
+ completedSteps: number;
858
+ failedSteps: number;
859
+ pendingSteps: number;
860
+ avgStepDurationMs: number;
861
+ capabilityUsage: Record<CapabilityType, number>;
862
+ };
863
+ }
864
+ declare function mapToolToCapability(toolName: string): CapabilityType;
865
+
866
+ interface AgentBehaviorPolicy {
867
+ retrieval: {
868
+ minReadWindowLines: number;
869
+ maxConsecutiveSmallWindowReadsPerFile: number;
870
+ smallFileReadAllThresholdLines: number;
871
+ };
872
+ noResult: {
873
+ minIterationsBeforeConclusion: number;
874
+ maxConsecutiveNoSignalSearchByTier: Record<LLMTier, number>;
875
+ };
876
+ evidence: {
877
+ minFilesReadForInformational: number;
878
+ minEvidenceDensityForInformational: number;
879
+ minInformationalResponseChars: number;
880
+ };
881
+ }
882
+ declare function createDefaultAgentBehaviorPolicy(): AgentBehaviorPolicy;
883
+
884
+ interface WorkspaceRepoNode {
885
+ name: string;
886
+ path: string;
887
+ reasons: string[];
888
+ /** Package names found inside each repo */
889
+ packages: string[];
890
+ /** Top-level directory names */
891
+ dirs: string[];
892
+ }
893
+ interface WorkspaceDiscoveryResult {
894
+ rootDir: string;
895
+ repos: WorkspaceRepoNode[];
896
+ }
897
+ declare function discoverWorkspace(rootDir: string): Promise<WorkspaceDiscoveryResult>;
898
+
899
+ /**
900
+ * StopConditionEvaluator — deterministic evaluation of all stop conditions.
901
+ *
902
+ * Checks ALL conditions in a single pass and returns the highest-priority match.
903
+ * Priority order: abort > report-complete > hard-budget > max-iterations > loop > no-tool-calls.
904
+ *
905
+ * Fixes bug: previously isLastIteration with tool calls (agent.ts:1247) triggered forced
906
+ * synthesis BEFORE checking for report tool (agent.ts:1462). Now REPORT_COMPLETE has
907
+ * priority 1 while MAX_ITERATIONS has priority 3.
908
+ *
909
+ * Custom SDK conditions are evaluated alongside built-ins. Priority >= 10 is reserved
910
+ * for custom conditions (built-ins use 0–5).
911
+ */
912
+
913
+ /**
914
+ * Minimal interface for LLM response — only what StopConditionEvaluator needs.
915
+ */
916
+ interface StopEvalResponse {
917
+ toolCalls?: Array<{
918
+ name: string;
919
+ input?: Record<string, unknown>;
920
+ }>;
921
+ content?: string;
922
+ }
923
+ /**
924
+ * Context needed to evaluate stop conditions.
925
+ */
926
+ interface StopEvalContext {
927
+ /** Current iteration (0-based) */
928
+ iteration: number;
929
+ /** Maximum allowed iterations */
930
+ maxIterations: number;
931
+ /** AbortSignal from user / parent agent */
932
+ abortSignal?: AbortSignal;
933
+ /** Total tokens consumed so far */
934
+ totalTokens: number;
935
+ /** Hard token limit (0 = no limit) */
936
+ hardTokenLimit: number;
937
+ /** Whether a loop was detected this iteration */
938
+ loopDetected: boolean;
939
+ }
940
+ /**
941
+ * Evaluate all stop conditions and return the highest-priority match (if any).
942
+ *
943
+ * Returns null if no stop condition fires (execution should continue).
944
+ */
945
+ declare function evaluateStopConditions(ctx: StopEvalContext, response: StopEvalResponse): StopConditionResult | null;
946
+ /**
947
+ * StopConditionEvaluator class — stateless, injectable for testing.
948
+ *
949
+ * Pass custom `StopCondition[]` from agent-sdk (via AgentSDK.addStopCondition()) to
950
+ * extend evaluation with user-defined conditions. Built-in conditions use priorities 0–5;
951
+ * custom conditions should use priority >= 10 to avoid overriding built-ins.
952
+ */
953
+ declare class StopConditionEvaluator {
954
+ private readonly customConditions;
955
+ constructor(customConditions?: StopCondition[]);
956
+ evaluate(ctx: StopEvalContext, response: StopEvalResponse,
957
+ /** Full RunContext — passed to SDK custom conditions (optional for backward compat) */
958
+ runCtx?: RunContext,
959
+ /** Full LLM result — passed to SDK custom conditions (optional for backward compat) */
960
+ llmResult?: LLMCallResult): StopConditionResult | null;
961
+ }
962
+
963
+ /**
964
+ * ExecutionLoop — the core iteration engine for Agent v2.
965
+ *
966
+ * Extracted from agent.ts `executeWithTier()` (lines 942–1650).
967
+ * Uses a callback-based LoopContext so the loop has no direct dependency
968
+ * on the Agent class itself (enabling independent unit testing).
969
+ *
970
+ * Key improvements over the agent.ts loop:
971
+ * 1. Returns LoopResult<T> — never throws TierEscalationSignal
972
+ * 2. Integrates StopConditionEvaluator for priority-based stop ordering
973
+ * 3. Supports ControlAction ('continue'|'stop'|'escalate') from guards
974
+ * 4. Iteration budget extension is supported via extendBudget callback
975
+ *
976
+ * Strangler fig pattern: agent.ts still owns the loop today.
977
+ * This class is built and tested in isolation first, then wired in.
978
+ */
979
+
980
+ /**
981
+ * A single LLM response — tool calls or final content.
982
+ */
983
+ interface LoopLLMResponse {
984
+ content: string;
985
+ toolCalls?: LoopToolCall[];
986
+ /** Token counts for this turn */
987
+ usage?: {
988
+ promptTokens: number;
989
+ completionTokens: number;
990
+ };
991
+ }
992
+ interface LoopToolCall {
993
+ id: string;
994
+ name: string;
995
+ input: Record<string, unknown>;
996
+ }
997
+ interface LoopToolResult {
998
+ toolCallId: string;
999
+ output: string;
1000
+ isError?: boolean;
1001
+ metadata?: Record<string, unknown>;
1002
+ }
1003
+ /**
1004
+ * Per-iteration snapshot passed to most callbacks.
1005
+ */
1006
+ interface IterationContext {
1007
+ iteration: number;
1008
+ maxIterations: number;
1009
+ totalTokens: number;
1010
+ isLastIteration: boolean;
1011
+ }
1012
+ /**
1013
+ * Everything the loop needs — implemented by the Agent (or a test stub).
1014
+ *
1015
+ * Callbacks are kept minimal: only what changes behaviour.
1016
+ * Side-effect-only hooks (tracing, metrics, events) are optional.
1017
+ */
1018
+ interface LoopContext<TResult> {
1019
+ /** Initial max iterations. Loop may call extendBudget() to raise this. */
1020
+ maxIterations: number;
1021
+ /**
1022
+ * Called once per iteration to get the LLM response.
1023
+ * Receives accumulated token count for budget decisions.
1024
+ */
1025
+ callLLM(ctx: IterationContext): Promise<LoopLLMResponse>;
1026
+ /**
1027
+ * Execute all tool calls for this iteration.
1028
+ * Returns results in the same order as calls.
1029
+ */
1030
+ executeTools(calls: LoopToolCall[], ctx: IterationContext): Promise<LoopToolResult[]>;
1031
+ /**
1032
+ * Build the final TaskResult from a report call or natural stop.
1033
+ */
1034
+ buildResult(answer: string, ctx: IterationContext, reasonCode: string): Promise<TResult>;
1035
+ /**
1036
+ * True when the agent's AbortController has been signalled.
1037
+ */
1038
+ isAborted(): boolean;
1039
+ /**
1040
+ * Returns the report answer if any tool call in the list is 'report'.
1041
+ * Returns undefined otherwise.
1042
+ */
1043
+ extractReportAnswer(calls: LoopToolCall[]): string | undefined;
1044
+ /**
1045
+ * Returns the reason string if the same tool calls have looped 3x.
1046
+ */
1047
+ detectLoop(calls: LoopToolCall[]): string | undefined;
1048
+ /**
1049
+ * Returns the escalation reason if the loop should escalate tier.
1050
+ * Called after tool execution each iteration.
1051
+ */
1052
+ evaluateEscalation(ctx: IterationContext): {
1053
+ shouldEscalate: boolean;
1054
+ reason: string;
1055
+ } | null;
1056
+ /**
1057
+ * Token hard limit: returns stop reason if limit is exceeded.
1058
+ * Called before LLM call each iteration.
1059
+ */
1060
+ checkHardTokenLimit(totalTokens: number): string | undefined;
1061
+ /**
1062
+ * Called after tool execution. May return a higher budget than current.
1063
+ * Return current maxIterations to keep unchanged.
1064
+ */
1065
+ extendBudget(ctx: IterationContext): number;
1066
+ /** Called at the start of each iteration (for events/tracing). */
1067
+ onIterationStart?(ctx: IterationContext): void;
1068
+ /** Called at the end of each iteration (for events/tracing). */
1069
+ onIterationEnd?(ctx: IterationContext, hadToolCalls: boolean): void;
1070
+ /** Called when a stop condition fires. */
1071
+ onStop?(condition: StopConditionResult, ctx: IterationContext): void;
1072
+ /** Called when token count updates after LLM response. */
1073
+ onTokensConsumed?(delta: number, total: number): void;
1074
+ }
1075
+ /**
1076
+ * Runs the agent iteration loop and returns a LoopResult.
1077
+ *
1078
+ * Never throws — tier escalation is returned as `{ outcome: 'escalate' }`.
1079
+ * All other errors are caught and returned as `{ outcome: 'complete', result: failureResult }`.
1080
+ */
1081
+ declare class ExecutionLoop<TResult> {
1082
+ private readonly ctx;
1083
+ private readonly evaluator;
1084
+ constructor(ctx: LoopContext<TResult>);
1085
+ run(): Promise<LoopResult<TResult>>;
1086
+ }
1087
+
1088
+ /**
1089
+ * LinearExecutionLoop — SDK-native implementation of ExecutionLoop.
1090
+ *
1091
+ * Implements the agent-sdk ExecutionLoop interface:
1092
+ * run(ctx: LoopContext): Promise<LoopResult>
1093
+ *
1094
+ * LoopContext (from agent-sdk) provides only infrastructure primitives:
1095
+ * - run: RunContext — shared run state
1096
+ * - appendMessage() — only way to mutate message history
1097
+ * - callLLM() — LLM call with middleware pipeline applied
1098
+ * - executeTools() — tool execution with guards + processors applied
1099
+ *
1100
+ * All business logic (stop detection, report extraction, loop detection)
1101
+ * lives here — not in LoopContext. This keeps the contract minimal.
1102
+ *
1103
+ * Stop priority order (lower number = higher priority):
1104
+ * 0 ABORT — abortSignal fired
1105
+ * 1 REPORT — agent called the report tool
1106
+ * 2 HARD_BUDGET — token hard limit (via BudgetMiddleware metadata)
1107
+ * 3 MAX_ITER — hit maxIterations
1108
+ * 4 LOOP — same 3 tool calls repeated twice
1109
+ * 5 NO_TOOLS — LLM returned no tool calls (natural completion)
1110
+ *
1111
+ * Returns LoopOutput — AgentRunner builds the full TaskResult from it.
1112
+ */
1113
+
1114
+ declare class LinearExecutionLoop implements ExecutionLoop$1 {
1115
+ run(ctx: LoopContext$1): Promise<LoopResult$1>;
1116
+ }
1117
+
1118
+ /**
1119
+ * Builds the system prompt for the agent LLM call.
1120
+ *
1121
+ * Architecture inspired by Claude Code:
1122
+ * - Static sections (core rules, workflow) — stable across sessions, cacheable
1123
+ * - Semi-static sections (working dir, delegation) — stable within session
1124
+ * - Dynamic sections (memory, facts, workspace) — change per iteration
1125
+ *
1126
+ * Tool descriptions are NOT in the system prompt — they go via API `tools:` parameter.
1127
+ * Project instructions (AGENTS.md) loaded conditionally based on task scope.
1128
+ */
1129
+
1130
+ interface SystemPromptInput {
1131
+ workingDir: string;
1132
+ responseMode: 'auto' | 'brief' | 'deep';
1133
+ isSubAgent: boolean;
1134
+ sessionId?: string;
1135
+ sessionRootDir?: string;
1136
+ currentTask?: string;
1137
+ memory?: AgentMemory;
1138
+ workspaceDiscovery?: WorkspaceDiscoveryResult | null;
1139
+ factSheetContent?: string;
1140
+ archiveSummaryHint?: string;
1141
+ }
1142
+ declare class SystemPromptBuilder {
1143
+ build(input: SystemPromptInput): Promise<string>;
1144
+ }
1145
+ declare function loadProjectInstructions(workingDir: string): string | null;
1146
+
1147
+ /**
1148
+ * Run Metrics Emitter
1149
+ *
1150
+ * Fires analytics events for run KPIs, tool outcomes, tier escalations,
1151
+ * and detects quality regressions using EMA baselines.
1152
+ */
1153
+ interface KpiBaseline {
1154
+ driftRateEma: number;
1155
+ evidenceDensityEma: number;
1156
+ toolErrorRateEma: number;
1157
+ samples: number;
1158
+ tokenHistory: number[];
1159
+ iterationUtilizationHistory: number[];
1160
+ qualityScoreHistory: number[];
1161
+ }
1162
+ interface TierEscalation {
1163
+ from: string;
1164
+ to: string;
1165
+ reason: string;
1166
+ iteration: number;
1167
+ }
1168
+ interface RegressionMetrics {
1169
+ driftRate: number;
1170
+ evidenceDensity: number;
1171
+ toolErrorRate: number;
1172
+ tokensUsed: number;
1173
+ iterationsUsed: number;
1174
+ iterationBudget: number;
1175
+ iterationUtilization: number;
1176
+ qualityScore: number;
1177
+ qualityGateStatus: string;
1178
+ }
1179
+ interface RunKpiPayload {
1180
+ sessionId?: string;
1181
+ agentId: string;
1182
+ task: string;
1183
+ success: boolean;
1184
+ error?: string;
1185
+ summaryPreview: string;
1186
+ iterationsUsed: number;
1187
+ iterationBudget: number;
1188
+ tokenBudget?: number;
1189
+ tokenUtilization?: number;
1190
+ startTier: string;
1191
+ finalTier: string;
1192
+ durationMs: number;
1193
+ tokensUsed: number;
1194
+ toolCallsTotal: number;
1195
+ toolSuccessCount: number;
1196
+ toolErrorCount: number;
1197
+ todoToolCalls: number;
1198
+ filesReadCount: number;
1199
+ filesModifiedCount: number;
1200
+ filesCreatedCount: number;
1201
+ driftDomainCount: number;
1202
+ driftDomains: string[];
1203
+ executionPhase: string;
1204
+ phaseDurationsMs: Record<string, number>;
1205
+ phaseTransitionCount: number;
1206
+ phaseTransitions: unknown[];
1207
+ ledger: {
1208
+ failedSteps: number;
1209
+ pendingSteps: number;
1210
+ };
1211
+ qualityGate: {
1212
+ status: string;
1213
+ score: number;
1214
+ reasons: string[];
1215
+ } | null;
1216
+ /** Fraction of iterations that repeated intent without new evidence. */
1217
+ repeatNoEvidenceRate?: number;
1218
+ }
1219
+ interface ToolOutcomeInput {
1220
+ toolName: string;
1221
+ success: boolean;
1222
+ durationMs: number;
1223
+ errorCode?: string;
1224
+ retryable?: boolean;
1225
+ }
1226
+ interface KpiBaselinePersister {
1227
+ getKpiBaseline(sessionId: string): Promise<KpiBaseline | null>;
1228
+ updateKpiBaseline(sessionId: string, fn: () => {
1229
+ version: number;
1230
+ updatedAt: string;
1231
+ driftRateEma: number;
1232
+ evidenceDensityEma: number;
1233
+ toolErrorRateEma: number;
1234
+ samples: number;
1235
+ tokenHistory: number[];
1236
+ iterationUtilizationHistory: number[];
1237
+ qualityScoreHistory: number[];
1238
+ }): Promise<void>;
1239
+ }
1240
+ interface EmitContext {
1241
+ analytics: {
1242
+ track(event: string, payload: unknown): Promise<void>;
1243
+ } | null;
1244
+ sessionId?: string;
1245
+ persister?: KpiBaselinePersister;
1246
+ baselineKey: string;
1247
+ log: (msg: string) => void;
1248
+ }
1249
+ /** For testing: clear the process-level baseline map */
1250
+ declare function clearProcessKpiBaselines(): void;
1251
+ declare class RunMetricsEmitter {
1252
+ readonly tierEscalations: TierEscalation[];
1253
+ recordTierEscalation(from: string, to: string, reason: string, iteration: number, ctx: {
1254
+ analytics: EmitContext['analytics'];
1255
+ sessionId?: string;
1256
+ agentId: string;
1257
+ task: string;
1258
+ tierEscalatedEvent: string;
1259
+ log: (msg: string) => void;
1260
+ }): Promise<void>;
1261
+ trackToolOutcome(input: ToolOutcomeInput, ctx: {
1262
+ analytics: EmitContext['analytics'];
1263
+ toolCalledEvent: string;
1264
+ log: (msg: string) => void;
1265
+ }): void;
1266
+ emitRunKpis(payload: RunKpiPayload, ctx: EmitContext): Promise<void>;
1267
+ private emitQualityRegressionEvent;
1268
+ reset(): void;
1269
+ }
1270
+ declare function getKpiBaselineKey(sessionRootDir: string, workingDir: string): string;
1271
+ declare function detectQualityRegression(metrics: RegressionMetrics, baseline: KpiBaseline): {
1272
+ regressed: boolean;
1273
+ reasons: string[];
1274
+ };
1275
+ declare function updateKpiBaseline(baseline: KpiBaseline, metrics: RegressionMetrics, alpha?: number): KpiBaseline;
1276
+ declare function extractToolErrorCode(result: {
1277
+ success: boolean;
1278
+ error?: string;
1279
+ errorDetails?: {
1280
+ code?: string;
1281
+ retryable?: boolean;
1282
+ };
1283
+ metadata?: Record<string, unknown>;
1284
+ }): string | null;
1285
+
1286
+ /**
1287
+ * MiddlewarePipeline — orchestrates middleware execution with error handling.
1288
+ *
1289
+ * Features:
1290
+ * - Runs before-hooks ascending (lower order = first), after-hooks descending
1291
+ * - Per-middleware failure policy: fail-open (log + skip) or fail-closed (stop)
1292
+ * - Per-hook timeout enforcement (0 = no timeout)
1293
+ */
1294
+
1295
+ interface PipelineOptions {
1296
+ featureFlags: FeatureFlags;
1297
+ onError?: (middlewareName: string, hookName: string, error: unknown) => void;
1298
+ }
1299
+ declare class MiddlewarePipeline {
1300
+ private readonly asc;
1301
+ private readonly desc;
1302
+ private readonly options;
1303
+ constructor(middlewares: AgentMiddleware[], options: PipelineOptions);
1304
+ private getActive;
1305
+ private runHook;
1306
+ onStart(ctx: RunContext): Promise<void>;
1307
+ onStop(ctx: RunContext, reason: string): Promise<void>;
1308
+ onComplete(ctx: RunContext): Promise<void>;
1309
+ beforeIteration(ctx: RunContext): Promise<ControlAction>;
1310
+ afterIteration(ctx: RunContext): Promise<void>;
1311
+ /** Merges patches from all middlewares in order (last wins per field) */
1312
+ beforeLLMCall(ctx: LLMCtx): Promise<LLMCallPatch>;
1313
+ afterLLMCall(ctx: LLMCtx, result: LLMCallResult): Promise<void>;
1314
+ /** Returns 'skip' if ANY middleware votes skip */
1315
+ beforeToolExec(ctx: ToolExecCtx): Promise<'execute' | 'skip'>;
1316
+ afterToolExec(ctx: ToolExecCtx, result: ToolOutput): Promise<void>;
1317
+ }
1318
+
1319
+ /**
1320
+ * ObservabilityMiddleware — bridges AgentMiddleware hooks to AgentEvent stream.
1321
+ *
1322
+ * Architecture (bus pattern):
1323
+ * - Lifecycle hooks (beforeLLMCall, afterLLMCall, beforeToolExec, afterToolExec)
1324
+ * emit into ctx.run.eventBus — infrastructure-level events.
1325
+ * - onStart subscribes to eventBus and translates to UI AgentEvent stream
1326
+ * via the onEvent callback (CLI, WebSocket, NDJSON trace).
1327
+ * - onStart/onStop emit agent:start / agent:end directly to onEvent
1328
+ * (lifecycle events don't go through the bus — they are one-per-run).
1329
+ *
1330
+ * Design:
1331
+ * - order = 5 (runs before all other middlewares so events fire first)
1332
+ * - failPolicy = 'fail-open' (observability NEVER breaks execution)
1333
+ *
1334
+ * File tracking (via run.meta):
1335
+ * - 'files' namespace, keys: 'read', 'modified', 'created'
1336
+ * - Populated by afterToolExec based on tool name + input
1337
+ * - SDKAgentRunner reads these at the end to build TaskResult
1338
+ */
1339
+
1340
+ declare class ObservabilityMiddleware {
1341
+ private readonly agentId;
1342
+ private readonly parentAgentId;
1343
+ private readonly sessionId;
1344
+ private readonly onEvent;
1345
+ readonly name = "observability";
1346
+ readonly order = 5;
1347
+ readonly config: {
1348
+ failPolicy: "fail-open";
1349
+ };
1350
+ private _startedAt;
1351
+ private _llmStartedAt;
1352
+ private _toolStartedAt;
1353
+ private _lastIteration;
1354
+ private _totalTokens;
1355
+ private _pendingDebug;
1356
+ /** Extra metadata injected before onStart — set by runner after workspace discovery. */
1357
+ startMeta?: {
1358
+ budget?: {
1359
+ maxTokens: number;
1360
+ softLimitRatio: number;
1361
+ hardLimitRatio: number;
1362
+ };
1363
+ workspaceTopology?: string[];
1364
+ workingDir?: string;
1365
+ };
1366
+ constructor(agentId: string, parentAgentId: string | undefined, sessionId: string | undefined, onEvent: AgentEventCallback | undefined);
1367
+ onStart(ctx: RunContext): void;
1368
+ onStop(ctx: RunContext, reason: string): void;
1369
+ beforeIteration(ctx: RunContext): 'continue';
1370
+ afterIteration(ctx: RunContext): void;
1371
+ beforeLLMCall(ctx: LLMCtx): undefined;
1372
+ afterLLMCall(ctx: LLMCtx, result: LLMCallResult): void;
1373
+ beforeToolExec(ctx: ToolExecCtx): 'execute';
1374
+ afterToolExec(ctx: ToolExecCtx, result: ToolOutput): void;
1375
+ private _emit;
1376
+ }
1377
+
1378
+ /**
1379
+ * ChangeTrackingMiddleware — captures file change snapshots via the SDK pipeline.
1380
+ *
1381
+ * Design:
1382
+ * - order = 8 (after Observability=5, before Budget=10)
1383
+ * - failPolicy = 'fail-open': tracking errors never break execution
1384
+ * - Intercepts afterToolExec for fs_write / fs_patch / fs_delete
1385
+ * - Tools must include before/after content in result.metadata (see filesystem.ts)
1386
+ * - Persists FileChange snapshots via ChangeStore
1387
+ * - Writes FileChangeSummary[] to RunContext.meta('changes', 'summaries') per run
1388
+ * - SDKAgentRunner reads meta at end to populate TaskResult.fileChanges
1389
+ *
1390
+ * Data flow:
1391
+ * fs_write/fs_patch/fs_delete
1392
+ * → result.metadata.changeSnapshot: { operation, beforeContent?, afterContent, ...opMeta }
1393
+ * → ChangeTrackingMiddleware.afterToolExec
1394
+ * → ChangeStore.save()
1395
+ * → meta('changes', 'summaries') += FileChangeSummary
1396
+ */
1397
+
1398
+ interface ChangeSnapshot {
1399
+ operation: 'write' | 'patch' | 'delete';
1400
+ /** File content before the operation (undefined = new file) */
1401
+ beforeContent?: string;
1402
+ /** File content after the operation */
1403
+ afterContent: string;
1404
+ isOverwrite?: boolean;
1405
+ startLine?: number;
1406
+ endLine?: number;
1407
+ linesAdded?: number;
1408
+ linesRemoved?: number;
1409
+ wasDeleted?: boolean;
1410
+ }
1411
+ interface ChangeTrackingConfig {
1412
+ agentId: string;
1413
+ sessionId: string | undefined;
1414
+ workingDir: string;
1415
+ storageConfig?: Partial<StorageConfig>;
1416
+ }
1417
+ declare class ChangeTrackingMiddleware {
1418
+ readonly name = "change-tracking";
1419
+ readonly order = 8;
1420
+ readonly config: {
1421
+ failPolicy: "fail-open";
1422
+ };
1423
+ private readonly store;
1424
+ private readonly agentId;
1425
+ private readonly sessionId;
1426
+ /** Current run ID — set on onStart, used to tag all changes in this run */
1427
+ private runId;
1428
+ constructor(cfg: ChangeTrackingConfig);
1429
+ onStart(ctx: RunContext): void;
1430
+ afterToolExec(ctx: ToolExecCtx, result: ToolOutput): Promise<void>;
1431
+ /** Rollback all changes captured in the current run — useful for undo-run */
1432
+ rollbackRun(sessionId: string, workingDir: string): Promise<{
1433
+ rolledBack: number;
1434
+ errors: string[];
1435
+ }>;
1436
+ /** Expose store for external queries (CLI, REST handlers) */
1437
+ get changeStore(): ChangeStore;
1438
+ }
1439
+ declare function getFileChangeSummaries(ctx: RunContext): FileChangeSummary$1[];
1440
+
1441
+ /**
1442
+ * BudgetMiddleware — token budget enforcement and convergence nudging.
1443
+ *
1444
+ * Hook points:
1445
+ * - beforeIteration: check hard limit → return 'stop' if exceeded
1446
+ * - beforeLLMCall: inject convergence nudge at soft limit via LLMCallPatch
1447
+ *
1448
+ * Token counter is injected via a simple getter — the runner owns totalTokens
1449
+ * and passes a closure. Everything else (policy, nudge state) lives here.
1450
+ */
1451
+
1452
+ interface BudgetPolicy {
1453
+ active: boolean;
1454
+ maxTokens: number;
1455
+ softLimitRatio: number;
1456
+ hardLimitRatio: number;
1457
+ hardStop: boolean;
1458
+ forceSynthesisOnHardLimit: boolean;
1459
+ }
1460
+ declare class BudgetMiddleware {
1461
+ readonly name = "budget";
1462
+ readonly order = 10;
1463
+ readonly config: {
1464
+ failPolicy: "fail-closed";
1465
+ timeoutMs: number;
1466
+ };
1467
+ private readonly policy;
1468
+ private readonly getTokensUsed;
1469
+ private _convergenceNudgeSent;
1470
+ /** Token deltas per iteration for diminishing returns detection */
1471
+ private _iterationDeltas;
1472
+ private _lastIterTokens;
1473
+ private _diminishingNudgeSent;
1474
+ constructor(policy: BudgetPolicy, getTokensUsed: () => number);
1475
+ get convergenceNudgeSent(): boolean;
1476
+ beforeIteration(ctx: RunContext): ControlAction;
1477
+ beforeLLMCall(ctx: LLMCtx): LLMCallPatch | undefined;
1478
+ reset(): void;
1479
+ }
1480
+
1481
+ /**
1482
+ * ProgressMiddleware — stuck detection and loop detection.
1483
+ *
1484
+ * Hook points:
1485
+ * - beforeIteration: detect stuck state (too many iterations without progress)
1486
+ * - afterToolExec: reset counter on successful tool, detect repeated call loops
1487
+ *
1488
+ * All state lives here. Callbacks are optional observers (for logging/telemetry).
1489
+ * fail-open: progress tracking errors never break execution.
1490
+ */
1491
+
1492
+ interface ProgressCallbacks {
1493
+ onProgress?: (iteration: number) => void;
1494
+ onStuck?: (iteration: number, iterationsSinceProgress: number) => void;
1495
+ onLoop?: (iteration: number, repeatedCalls: string[]) => void;
1496
+ }
1497
+ declare class ProgressMiddleware {
1498
+ readonly name = "progress";
1499
+ readonly order = 50;
1500
+ readonly config: {
1501
+ failPolicy: "fail-open";
1502
+ timeoutMs: number;
1503
+ };
1504
+ private readonly stuckThreshold;
1505
+ private readonly callbacks;
1506
+ private _iterationsSinceProgress;
1507
+ private _recentToolCalls;
1508
+ private _lastIterNudgeSent;
1509
+ /** Tracks which stuck nudge level was last sent (0 = none) */
1510
+ private _stuckNudgeLevel;
1511
+ /** Tool calls since last plan_write (plan mode checkpoint tracking) */
1512
+ private _toolCallsSincePlanWrite;
1513
+ /** Whether plan checkpoint nudge was injected for current LLM call */
1514
+ private _planCheckpointPending;
1515
+ constructor(stuckThreshold?: number, callbacks?: ProgressCallbacks);
1516
+ get iterationsSinceProgress(): number;
1517
+ get isStuck(): boolean;
1518
+ beforeIteration(ctx: RunContext): ControlAction;
1519
+ beforeLLMCall(ctx: LLMCtx): LLMCallPatch | undefined;
1520
+ afterToolExec(ctx: ToolExecCtx, result: ToolOutput): void;
1521
+ /**
1522
+ * Read current plan.md from session directory.
1523
+ * Returns null if no plan file exists or session info unavailable.
1524
+ */
1525
+ private _readPlanFile;
1526
+ reset(): void;
1527
+ }
1528
+
1529
+ /**
1530
+ * AnalyticsMiddleware — pure observer for metrics and tool outcome tracking.
1531
+ *
1532
+ * Hook points:
1533
+ * - onStart: record start time
1534
+ * - afterLLMCall: record token usage
1535
+ * - afterToolExec: track tool outcomes
1536
+ * - onStop: emit run metrics
1537
+ *
1538
+ * fail-open: analytics should NEVER break execution.
1539
+ */
1540
+
1541
+ interface ToolOutcome {
1542
+ toolName: string;
1543
+ success: boolean;
1544
+ durationMs?: number;
1545
+ errorCode?: string;
1546
+ }
1547
+ interface RunMetrics {
1548
+ startedAt: number;
1549
+ endedAt: number;
1550
+ durationMs: number;
1551
+ totalIterations: number;
1552
+ toolOutcomes: ToolOutcome[];
1553
+ toolSuccessCount: number;
1554
+ toolErrorCount: number;
1555
+ stopReason?: string;
1556
+ }
1557
+ interface AnalyticsCallbacks {
1558
+ onRunComplete?: (metrics: RunMetrics) => void;
1559
+ onToolOutcome?: (outcome: ToolOutcome) => void;
1560
+ }
1561
+ declare class AnalyticsMiddleware {
1562
+ readonly name = "analytics";
1563
+ readonly order = 90;
1564
+ readonly config: {
1565
+ failPolicy: "fail-open";
1566
+ timeoutMs: number;
1567
+ };
1568
+ private readonly callbacks;
1569
+ private _startedAt;
1570
+ private _toolOutcomes;
1571
+ private _toolSuccessCount;
1572
+ private _toolErrorCount;
1573
+ private _lastIteration;
1574
+ constructor(callbacks?: AnalyticsCallbacks);
1575
+ get toolSuccessCount(): number;
1576
+ get toolErrorCount(): number;
1577
+ get toolOutcomes(): ReadonlyArray<ToolOutcome>;
1578
+ onStart(_ctx: RunContext): void;
1579
+ afterLLMCall(ctx: LLMCtx, result: LLMCallResult): void;
1580
+ afterToolExec(_ctx: ToolExecCtx, result: ToolOutput): void;
1581
+ onStop(_ctx: RunContext, reason: string): void;
1582
+ reset(): void;
1583
+ }
1584
+
1585
+ /**
1586
+ * ContextFilterMiddleware — sliding window + output truncation + optional compaction.
1587
+ *
1588
+ * Hook points:
1589
+ * - afterToolExec: (intentionally empty — rounds extracted from ctx.messages in beforeLLMCall)
1590
+ * - beforeLLMCall: return LLMCallPatch with filtered message window
1591
+ *
1592
+ * Design:
1593
+ * - order = 15 (after budget=10, so budget's convergence nudge is already appended)
1594
+ * - failPolicy = 'fail-open' (never breaks execution)
1595
+ * - The middleware slices the last N iterations on each LLM call.
1596
+ * - System messages and the initial user task are ALWAYS preserved.
1597
+ * - Tool outputs longer than maxOutputLength are truncated.
1598
+ * - When enableCompaction is true, dropped rounds are summarized by a small-tier LLM
1599
+ * instead of being silently removed.
1600
+ */
1601
+
1602
+ interface ContextFilterMwConfig {
1603
+ /** Max characters for tool output before truncation (default: 8000) */
1604
+ maxOutputLength?: number;
1605
+ /** Number of most-recent assistant+tool-result "rounds" to keep (default: 10) */
1606
+ slidingWindowSize?: number;
1607
+ /** When true, dropped rounds are summarized by LLM instead of silently removed (default: false) */
1608
+ enableCompaction?: boolean;
1609
+ }
1610
+ declare class ContextFilterMiddleware {
1611
+ readonly name = "context-filter";
1612
+ readonly order = 15;
1613
+ readonly config: {
1614
+ failPolicy: 'fail-open';
1615
+ timeoutMs: number;
1616
+ };
1617
+ private readonly maxOutputLength;
1618
+ private readonly slidingWindowSize;
1619
+ private readonly enableCompaction;
1620
+ private readonly rounds;
1621
+ private compactionCache;
1622
+ private llm;
1623
+ constructor(config?: ContextFilterMwConfig);
1624
+ afterToolExec(ctx: ToolExecCtx, result: ToolOutput): void;
1625
+ beforeLLMCall(ctx: LLMCtx): Promise<LLMCallPatch | undefined>;
1626
+ /**
1627
+ * Build a summary for dropped rounds.
1628
+ * With compaction enabled: LLM summarization (cached).
1629
+ * Without compaction: simple "[N earlier iterations removed]" note.
1630
+ */
1631
+ private buildDropSummary;
1632
+ /**
1633
+ * Call small-tier LLM to summarize dropped rounds into a compact block.
1634
+ */
1635
+ private summarizeRounds;
1636
+ /**
1637
+ * Microcompact: for rounds being kept (not dropped), clear tool result content
1638
+ * from old rounds while keeping assistant text. This reduces context size
1639
+ * without losing the agent's reasoning chain.
1640
+ *
1641
+ * Only applied to rounds older than `keepFullRounds` from the end.
1642
+ * The most recent rounds keep full tool outputs for immediate context.
1643
+ */
1644
+ private microcompact;
1645
+ private truncate;
1646
+ }
1647
+
1648
+ /**
1649
+ * TaskMiddleware — async task management for sub-agent operations.
1650
+ *
1651
+ * Manages a registry of async tasks submitted via task_submit tool.
1652
+ * Tasks run concurrently as sub-agents and can be polled (task_status)
1653
+ * or awaited (task_collect).
1654
+ *
1655
+ * Hook points:
1656
+ * beforeLLMCall → inject pending/completed task summary into Status Block
1657
+ * onStop → cancel all pending tasks
1658
+ *
1659
+ * order = 25 (after FactSheet=20, before Progress=50)
1660
+ */
1661
+
1662
+ interface TaskMiddlewareConfig {
1663
+ /** Max concurrent async tasks (default: 3) */
1664
+ maxConcurrent?: number;
1665
+ }
1666
+ type SpawnFn = (request: SpawnAgentRequest) => Promise<SpawnAgentResult>;
1667
+ declare class TaskMiddleware {
1668
+ readonly name = "task-manager";
1669
+ readonly order = 25;
1670
+ readonly config: {
1671
+ failPolicy: "fail-open";
1672
+ timeoutMs: number;
1673
+ };
1674
+ private readonly tasks;
1675
+ private readonly promises;
1676
+ private readonly maxConcurrent;
1677
+ private spawnFn;
1678
+ constructor(config?: TaskMiddlewareConfig);
1679
+ /** Set the spawn function (called by runner during setup) */
1680
+ setSpawnFn(fn: SpawnFn): void;
1681
+ /**
1682
+ * Submit a new async task. Returns immediately with a task ID.
1683
+ */
1684
+ submit(description: string, request: SpawnAgentRequest): Promise<AsyncTask>;
1685
+ /**
1686
+ * Get status of one or all tasks.
1687
+ */
1688
+ getStatus(taskId?: string): AsyncTask | AsyncTask[] | null;
1689
+ /**
1690
+ * Await a specific task and return its result.
1691
+ */
1692
+ collect(taskId: string): Promise<SpawnAgentResult>;
1693
+ /**
1694
+ * Inject task summary into context via meta (read by FactSheetMiddleware's Status Block).
1695
+ */
1696
+ beforeLLMCall(ctx: LLMCtx): LLMCallPatch | undefined;
1697
+ /**
1698
+ * On stop: no cleanup needed — promises resolve/reject on their own.
1699
+ */
1700
+ onStop(): void;
1701
+ }
1702
+
1703
+ /**
1704
+ * SearchSignalMiddleware — search quality tracking and early conclusion.
1705
+ *
1706
+ * Feature-flagged: enabled when FeatureFlags.searchSignal is true.
1707
+ */
1708
+
1709
+ interface SearchSignalMwState {
1710
+ searchSignalHits: number;
1711
+ lastSignalIteration: number;
1712
+ noResultStreak: number;
1713
+ }
1714
+ interface SearchSignalCallbacks {
1715
+ onSignalHit?: (toolName: string, iteration: number) => void;
1716
+ onNoResultStreak?: (streak: number, iteration: number) => void;
1717
+ }
1718
+ declare class SearchSignalMiddleware {
1719
+ readonly name = "search-signal";
1720
+ readonly order = 60;
1721
+ readonly config: {
1722
+ failPolicy: "fail-open";
1723
+ timeoutMs: number;
1724
+ };
1725
+ private readonly callbacks;
1726
+ private _hits;
1727
+ private _lastSignalIteration;
1728
+ private _noResultStreak;
1729
+ private _featureFlags?;
1730
+ constructor(callbacks?: SearchSignalCallbacks);
1731
+ enabled(): boolean;
1732
+ withFeatureFlags(flags: FeatureFlags): this;
1733
+ get state(): SearchSignalMwState;
1734
+ afterToolExec(ctx: ToolExecCtx, result: ToolOutput): void;
1735
+ onStop(): void;
1736
+ reset(): void;
1737
+ }
1738
+
1739
+ /**
1740
+ * TodoSyncMiddleware — todo list coordination and phase syncing.
1741
+ *
1742
+ * Feature-flagged: enabled when FeatureFlags.todoSync is true.
1743
+ */
1744
+
1745
+ interface TodoSyncCallbacks {
1746
+ onNudge?: (iteration: number) => void;
1747
+ onFinalize?: (success: boolean) => void;
1748
+ }
1749
+ declare class TodoSyncMiddleware {
1750
+ readonly name = "todo-sync";
1751
+ readonly order = 40;
1752
+ readonly config: {
1753
+ failPolicy: "fail-open";
1754
+ timeoutMs: number;
1755
+ };
1756
+ private readonly callbacks;
1757
+ private _nudgeSent;
1758
+ private _featureFlags?;
1759
+ constructor(callbacks?: TodoSyncCallbacks);
1760
+ enabled(): boolean;
1761
+ withFeatureFlags(flags: FeatureFlags): this;
1762
+ get nudgeSent(): boolean;
1763
+ beforeIteration(_ctx: RunContext): ControlAction;
1764
+ beforeLLMCall(ctx: LLMCtx): LLMCallPatch | undefined;
1765
+ onStop(_ctx: RunContext, reason: string): void;
1766
+ reset(): void;
1767
+ }
1768
+
1769
+ /**
1770
+ * ReflectionMiddleware — adaptive LLM-driven operational reflection.
1771
+ *
1772
+ * Feature-flagged: enabled when FeatureFlags.reflection is true.
1773
+ */
1774
+
1775
+ interface ReflectionCallbacks {
1776
+ onReflectionNeeded?: (trigger: string, iteration: number) => void;
1777
+ }
1778
+ declare class ReflectionMiddleware {
1779
+ readonly name = "reflection";
1780
+ readonly order = 70;
1781
+ readonly config: {
1782
+ failPolicy: "fail-open";
1783
+ timeoutMs: number;
1784
+ };
1785
+ private readonly callbacks;
1786
+ private _toolCallsSinceReflection;
1787
+ private _failedToolsSinceReflection;
1788
+ private readonly reflectionInterval;
1789
+ private _featureFlags?;
1790
+ constructor(callbacks?: ReflectionCallbacks, reflectionInterval?: number);
1791
+ enabled(): boolean;
1792
+ withFeatureFlags(flags: FeatureFlags): this;
1793
+ afterToolExec(ctx: ToolExecCtx, result: ToolOutput): void;
1794
+ onStop(): void;
1795
+ reset(): void;
1796
+ }
1797
+
1798
+ /**
1799
+ * TaskClassifierMiddleware — intent inference and scope extraction.
1800
+ *
1801
+ * Feature-flagged: enabled when FeatureFlags.taskClassifier is true.
1802
+ * Runs first (order=5) — classification informs other middlewares.
1803
+ *
1804
+ * Classification strategy:
1805
+ * 1. LLM tool calling (preferred) — language-agnostic, semantic, uses classify_task tool
1806
+ * 2. Heuristic regex fallback — instant, no LLM cost, English/Russian keywords
1807
+ */
1808
+
1809
+ type TaskIntent = 'action' | 'discovery' | 'analysis';
1810
+ interface TaskClassification {
1811
+ intent: TaskIntent;
1812
+ scope?: string;
1813
+ confidence: number;
1814
+ }
1815
+ interface TaskClassifierCallbacks {
1816
+ onClassified?: (classification: TaskClassification) => void;
1817
+ }
1818
+ declare class TaskClassifierMiddleware {
1819
+ readonly name = "task-classifier";
1820
+ readonly order = 5;
1821
+ readonly config: {
1822
+ failPolicy: "fail-open";
1823
+ timeoutMs: number;
1824
+ };
1825
+ private readonly callbacks;
1826
+ private _classification;
1827
+ private _featureFlags?;
1828
+ constructor(callbacks?: TaskClassifierCallbacks);
1829
+ enabled(): boolean;
1830
+ withFeatureFlags(flags: FeatureFlags): this;
1831
+ get classification(): TaskClassification | null;
1832
+ onStart(ctx: RunContext): Promise<void>;
1833
+ private classify;
1834
+ reset(): void;
1835
+ }
1836
+
1837
+ /**
1838
+ * ToolManager — single enforcement layer for tool registration and execution.
1839
+ *
1840
+ * Responsibilities:
1841
+ * 1. Register ToolPacks with namespace assignment and conflict resolution
1842
+ * 2. Resolve tool names (short or qualified) to the correct packed tool
1843
+ * 3. Enforce permissions (allowedPaths, deniedCommands, networkAllowed)
1844
+ * 4. Audit trail logging (when auditTrail: true)
1845
+ * 5. Delegate execution to the packed tool
1846
+ *
1847
+ * Tools and packs do NOT check permissions themselves — ToolManager does.
1848
+ */
1849
+
1850
+ interface ToolManagerOptions {
1851
+ /** Called before each tool execution when pack has auditTrail: true */
1852
+ onAudit?: (toolName: string, packId: string, input: Record<string, unknown>) => void;
1853
+ /** Called on registration errors (for logging) */
1854
+ onError?: (message: string) => void;
1855
+ }
1856
+ declare class ToolManager {
1857
+ private readonly packs;
1858
+ private readonly resolvedTools;
1859
+ private readonly options;
1860
+ constructor(options?: ToolManagerOptions);
1861
+ /**
1862
+ * Register a ToolPack. Handles namespace assignment and conflict resolution.
1863
+ * @throws If conflictPolicy is 'error' and a name collision occurs
1864
+ */
1865
+ register(pack: ToolPack): void;
1866
+ private resolveConflict;
1867
+ private findPackedTool;
1868
+ private createResolved;
1869
+ /**
1870
+ * Get all tool definitions matching the filter. Used to build the LLM tool list.
1871
+ */
1872
+ getDefinitions(filter?: ToolFilter): ToolDefinition[];
1873
+ /**
1874
+ * Get resolved tools matching the filter.
1875
+ */
1876
+ getTools(filter?: ToolFilter): ResolvedTool[];
1877
+ /**
1878
+ * Get a single tool by name (short or qualified).
1879
+ */
1880
+ getTool(name: string): ResolvedTool | undefined;
1881
+ /**
1882
+ * Get sorted list of tool names.
1883
+ */
1884
+ getToolNames(): string[];
1885
+ /**
1886
+ * Check if a tool exists.
1887
+ */
1888
+ hasTool(name: string): boolean;
1889
+ /**
1890
+ * Get all registered pack IDs.
1891
+ */
1892
+ getPackIds(): string[];
1893
+ /**
1894
+ * Execute a tool by name. This is the SINGLE enforcement layer:
1895
+ * 1. Resolve name → find tool
1896
+ * 2. Permission check (allowedPaths, deniedCommands, networkAllowed)
1897
+ * 3. Audit trail (if enabled)
1898
+ * 4. Delegate to pack executor
1899
+ */
1900
+ execute(name: string, input: Record<string, unknown>): Promise<ToolResult>;
1901
+ private checkPermissions;
1902
+ /**
1903
+ * Remove all resolved tools whose short name is NOT in the allowlist.
1904
+ * Used by sub-agents with preset-based tool restrictions.
1905
+ */
1906
+ applyAllowlist(allowedTools: string[]): void;
1907
+ /**
1908
+ * Initialize all registered packs.
1909
+ */
1910
+ initializeAll(): Promise<void>;
1911
+ /**
1912
+ * Dispose all registered packs.
1913
+ */
1914
+ disposeAll(): Promise<void>;
1915
+ }
1916
+
1917
+ /**
1918
+ * CoreToolPack — wraps the existing tool registry into a ToolPack.
1919
+ *
1920
+ * This provides backward compatibility: the current 22+ tools continue to
1921
+ * work through the new ToolManager, while new packs (kb-labs, MCP) can
1922
+ * be added alongside.
1923
+ *
1924
+ * The pack does NOT re-implement tools — it wraps the existing ToolRegistry.
1925
+ */
1926
+
1927
+ /**
1928
+ * Minimal interface matching ToolRegistry from agent-tools.
1929
+ * Avoids direct import to keep agent-core independent.
1930
+ */
1931
+ interface LegacyToolRegistry {
1932
+ getDefinitions(): Array<{
1933
+ type: 'function';
1934
+ function: {
1935
+ name: string;
1936
+ description: string;
1937
+ parameters: Record<string, unknown>;
1938
+ };
1939
+ }>;
1940
+ execute(name: string, input: Record<string, unknown>): Promise<ToolResult>;
1941
+ getToolNames(): string[];
1942
+ }
1943
+ /**
1944
+ * Create a CoreToolPack from an existing ToolRegistry.
1945
+ */
1946
+ declare function createCoreToolPack(registry: LegacyToolRegistry): ToolPack;
1947
+
1948
+ /**
1949
+ * KB Labs ToolPack — stub for future kb-labs-specific tools.
1950
+ *
1951
+ * Will include:
1952
+ * - mind-rag integration (semantic code search)
1953
+ * - workflow management tools
1954
+ * - plugin system integration
1955
+ *
1956
+ * Currently disabled by default — enabled via configuration.
1957
+ */
1958
+
1959
+ /**
1960
+ * Create the KB Labs ToolPack (stub — no tools yet).
1961
+ */
1962
+ declare function createKBLabsToolPack(): ToolPack;
1963
+
1964
+ /**
1965
+ * AsyncTaskToolPack — ToolPack wrapping task_submit / task_status / task_collect.
1966
+ *
1967
+ * Created by runner in executeWithTier and registered alongside CoreToolPack.
1968
+ * The pack holds a reference to the TaskMiddleware which owns the task registry.
1969
+ */
1970
+
1971
+ declare function createAsyncTaskToolPack(taskMw: TaskMiddleware): ToolPack;
1972
+
1973
+ /**
1974
+ * AgentRegistry — typed catalogue of agent presets.
1975
+ *
1976
+ * Each preset specifies:
1977
+ * - toolPacks: which ToolPacks the agent gets (namespaces, not concrete classes)
1978
+ * - featureFlags overrides (vs DEFAULT_FEATURE_FLAGS)
1979
+ * - systemPrompt suffix (appended after the base prompt)
1980
+ * - maxIterations default
1981
+ * - readOnly: prevents write tools being registered
1982
+ *
1983
+ * Presets ship with the platform; consumers can add custom types via register().
1984
+ *
1985
+ * Design: registry is purely a data store (no Agent instantiation here).
1986
+ * The Orchestrator reads registry to configure child agents.
1987
+ */
1988
+
1989
+ /**
1990
+ * Which tool packs an agent is allowed to load.
1991
+ * 'core' — read-only file system, search, memory tools
1992
+ * 'coder' — full FS (write, patch), shell execution
1993
+ * 'kb-labs' — kb-labs specific tools (mind-rag, workflow, etc.)
1994
+ */
1995
+ type ToolPackRef = 'core' | 'coder' | 'kb-labs' | string;
1996
+ /**
1997
+ * A registered agent type definition.
1998
+ */
1999
+ interface AgentTypeDefinition {
2000
+ /** Unique identifier, used when spawning: `registry.get('researcher')` */
2001
+ readonly id: string;
2002
+ /** Human-readable label */
2003
+ readonly label: string;
2004
+ /** Brief description shown in UIs and prompts */
2005
+ readonly description: string;
2006
+ /** Tool packs this agent is allowed to use */
2007
+ readonly toolPacks: ToolPackRef[];
2008
+ /** Feature flag overrides (merged on top of DEFAULT_FEATURE_FLAGS) */
2009
+ readonly featureFlags?: Partial<FeatureFlags>;
2010
+ /** Suffix appended to the base system prompt */
2011
+ readonly systemPromptSuffix?: string;
2012
+ /** Default max iterations (can be overridden at spawn time) */
2013
+ readonly maxIterations: number;
2014
+ /** If true, write/patch/exec tools are stripped from registered packs */
2015
+ readonly readOnly: boolean;
2016
+ /** Max spawn depth: 0 = cannot spawn children */
2017
+ readonly maxDepth: number;
2018
+ }
2019
+ declare class AgentRegistry {
2020
+ private readonly definitions;
2021
+ constructor();
2022
+ /**
2023
+ * Register a custom agent type or override a preset.
2024
+ * Throws if id is empty.
2025
+ */
2026
+ register(definition: AgentTypeDefinition): void;
2027
+ /**
2028
+ * Get an agent type definition by id.
2029
+ * Returns undefined if not found (callers should handle missing types).
2030
+ */
2031
+ get(id: string): AgentTypeDefinition | undefined;
2032
+ /**
2033
+ * Get an agent type definition, throwing if not found.
2034
+ */
2035
+ getOrThrow(id: string): AgentTypeDefinition;
2036
+ /**
2037
+ * List all registered agent type ids.
2038
+ */
2039
+ listIds(): string[];
2040
+ /**
2041
+ * List all registered agent type definitions.
2042
+ */
2043
+ list(): AgentTypeDefinition[];
2044
+ /**
2045
+ * Merge feature flags: DEFAULT + preset overrides.
2046
+ */
2047
+ resolveFeatureFlags(id: string): FeatureFlags;
2048
+ /**
2049
+ * Check whether a definition exists.
2050
+ */
2051
+ has(id: string): boolean;
2052
+ }
2053
+ /**
2054
+ * Singleton registry for the process (can be replaced in tests).
2055
+ */
2056
+ declare const globalAgentRegistry: AgentRegistry;
2057
+
2058
+ /**
2059
+ * ParallelExecutor — resource-governed parallel sub-agent execution.
2060
+ *
2061
+ * Features:
2062
+ * - Concurrency cap: at most `maxConcurrent` agents run simultaneously
2063
+ * - Cancel tree: parent AbortController propagates to all children
2064
+ * - Budget partition: parent token budget split across children (equal | weighted)
2065
+ * - Dedupe key: identical tasks share one execution
2066
+ * - Backpressure: queue size cap, reject or wait when queue is full
2067
+ * - Join timeout: collect results up to `joinTimeoutMs`, return partial on timeout
2068
+ * - MaxDepth: prevents runaway recursion
2069
+ *
2070
+ * The executor is generic: it calls a `runner` function you provide,
2071
+ * so it has no direct dependency on the Agent class (testable in isolation).
2072
+ */
2073
+ interface SubAgentRequest {
2074
+ /** Unique task description */
2075
+ task: string;
2076
+ /** Agent type from registry (default: 'researcher') */
2077
+ agentType?: string;
2078
+ /** Override max iterations for this specific spawn */
2079
+ maxIterations?: number;
2080
+ /** Working directory (relative to project root) */
2081
+ workingDir?: string;
2082
+ /**
2083
+ * Dedup key — if another pending/running task has the same key,
2084
+ * this request joins that result instead of spawning a new agent.
2085
+ * Default: task string itself.
2086
+ */
2087
+ dedupeKey?: string;
2088
+ /** Weight for budget partition (1 = equal share). Default: 1 */
2089
+ weight?: number;
2090
+ }
2091
+ interface SubAgentResult {
2092
+ task: string;
2093
+ agentType: string;
2094
+ success: boolean;
2095
+ result: string;
2096
+ iterations: number;
2097
+ tokensUsed: number;
2098
+ /** True if this result came from a deduped execution */
2099
+ deduped?: boolean;
2100
+ /** Error message if success=false */
2101
+ error?: string;
2102
+ /** Whether the result was collected before timeout */
2103
+ timedOut?: boolean;
2104
+ }
2105
+ type TokenPartitionStrategy = 'equal' | 'weighted';
2106
+ interface ParallelExecutorConfig {
2107
+ /** Maximum simultaneously running agents. Default: 5 */
2108
+ maxConcurrent: number;
2109
+ /** Maximum queue size (pending, not running). Reject beyond this. Default: 20 */
2110
+ maxQueueSize: number;
2111
+ /** Maximum nesting depth. 0 = cannot spawn. Default: 3 */
2112
+ maxDepth: number;
2113
+ /** Maximum time to wait for all agents to complete (ms). Default: 120_000 */
2114
+ joinTimeoutMs: number;
2115
+ /** How to split token budget across children. Default: 'equal' */
2116
+ tokenPartition: TokenPartitionStrategy;
2117
+ /** Total token budget available to share (0 = unlimited). Default: 0 */
2118
+ parentTokenBudget: number;
2119
+ }
2120
+ /**
2121
+ * Function signature for actually running a sub-agent.
2122
+ * Injected by the caller (Agent or test stub).
2123
+ */
2124
+ type AgentRunner = (request: SubAgentRequest, tokenBudget: number, signal: AbortSignal) => Promise<SubAgentResult>;
2125
+ declare class ParallelExecutor {
2126
+ private readonly config;
2127
+ private readonly runner;
2128
+ private readonly parentSignal;
2129
+ /** Currently running slot count */
2130
+ private running;
2131
+ /** Pending tasks waiting for a slot */
2132
+ private readonly queue;
2133
+ /** Dedup map: key → shared promise */
2134
+ private readonly dedupeMap;
2135
+ constructor(runner: AgentRunner, parentSignal: AbortSignal, config?: Partial<ParallelExecutorConfig>);
2136
+ /**
2137
+ * Submit a batch of requests and collect results.
2138
+ * Returns when all finish or joinTimeoutMs elapses (whichever comes first).
2139
+ * Timed-out tasks have `timedOut: true` in their result.
2140
+ */
2141
+ executeAll(requests: SubAgentRequest[], depth?: number): Promise<SubAgentResult[]>;
2142
+ /**
2143
+ * Submit a single request.
2144
+ * Handles dedup, backpressure, and concurrency limit.
2145
+ */
2146
+ submit(req: SubAgentRequest, tokenBudget: number, depth?: number): Promise<SubAgentResult>;
2147
+ private enqueue;
2148
+ private drainQueue;
2149
+ private runOne;
2150
+ private partitionBudget;
2151
+ private joinWithTimeout;
2152
+ /** Return current running/queue stats for monitoring. */
2153
+ stats(): {
2154
+ running: number;
2155
+ queued: number;
2156
+ deduped: number;
2157
+ };
2158
+ }
2159
+
2160
+ type DelegationStrategy = 'auto' | 'sequential' | 'parallel';
2161
+ interface OrchestratorConfig {
2162
+ /** Strategy for delegating subtasks. Default: 'sequential' */
2163
+ strategy: DelegationStrategy;
2164
+ /** ParallelExecutor config (used for 'parallel' and 'auto' strategies) */
2165
+ executor: Partial<ParallelExecutorConfig>;
2166
+ /** Agent registry (default: globalAgentRegistry) */
2167
+ registry?: AgentRegistry;
2168
+ /** Current depth (incremented for each level of nesting) */
2169
+ depth: number;
2170
+ }
2171
+ /**
2172
+ * A single spawn request from the tool layer.
2173
+ * Used by TaskMiddleware's spawnFn to create sub-agents.
2174
+ */
2175
+ interface LegacySpawnRequest {
2176
+ task: string;
2177
+ maxIterations?: number;
2178
+ workingDir?: string;
2179
+ }
2180
+ /**
2181
+ * Result returned to the tool layer.
2182
+ */
2183
+ interface LegacySpawnResult {
2184
+ success: boolean;
2185
+ result: string;
2186
+ iterations: number;
2187
+ tokensUsed: number;
2188
+ }
2189
+ declare class SubAgentOrchestrator {
2190
+ private readonly config;
2191
+ private readonly registry;
2192
+ private readonly executor;
2193
+ constructor(runner: AgentRunner, parentSignal: AbortSignal, config?: Partial<OrchestratorConfig>);
2194
+ /**
2195
+ * Spawn a single sub-agent.
2196
+ */
2197
+ spawnOne(req: LegacySpawnRequest): Promise<LegacySpawnResult>;
2198
+ /**
2199
+ * Spawn multiple sub-agents using the configured strategy.
2200
+ */
2201
+ spawnMany(requests: SubAgentRequest[], strategy?: DelegationStrategy): Promise<SubAgentResult[]>;
2202
+ /**
2203
+ * Resolve an agent type definition from the registry.
2204
+ * Returns undefined if agentType is not registered.
2205
+ */
2206
+ resolveAgentType(agentType: string): AgentTypeDefinition | undefined;
2207
+ /**
2208
+ * Return executor stats for monitoring.
2209
+ */
2210
+ stats(): {
2211
+ running: number;
2212
+ queued: number;
2213
+ deduped: number;
2214
+ };
2215
+ private toLegacyResult;
2216
+ }
2217
+
2218
+ /**
2219
+ * bootstrapAgentSDK — registers SDKAgentRunner as the RunnerFactory.
2220
+ *
2221
+ * Call this exactly once at application startup, before any sdk.createRunner() call.
2222
+ * Typically called at the top of the CLI entry point (agent:run command).
2223
+ *
2224
+ * @example
2225
+ * import { bootstrapAgentSDK } from '@kb-labs/agent-core';
2226
+ * bootstrapAgentSDK();
2227
+ * const runner = new AgentSDK().register(pack).createRunner(config);
2228
+ */
2229
+ declare function bootstrapAgentSDK(): void;
2230
+
2231
+ /**
2232
+ * SDKAgentRunner — clean agent implementation via AgentSDK.
2233
+ *
2234
+ * Composition:
2235
+ * AgentSDK state → SDKAgentRunner → ObservabilityMiddleware (events + file tracking)
2236
+ * → BudgetMiddleware (token budget enforcement)
2237
+ * → ContextFilterMiddleware (sliding window + truncation)
2238
+ * → FactSheetMiddleware (structured working memory)
2239
+ * → ProgressMiddleware (stuck/loop detection)
2240
+ * → TodoSyncMiddleware (feature-flagged)
2241
+ * → SearchSignalMiddleware (feature-flagged)
2242
+ * → ReflectionMiddleware (feature-flagged)
2243
+ * → TaskClassifierMiddleware (feature-flagged)
2244
+ * → MiddlewarePipeline
2245
+ * → ToolManager (ToolPacks)
2246
+ * → ToolExecutor (guards + processors)
2247
+ * → LoopContextImpl
2248
+ * → ExecutionLoop (LinearExecutionLoop)
2249
+ * → SubAgentOrchestrator (child agents)
2250
+ *
2251
+ * Tier escalation:
2252
+ * - Runner iterates small → medium → large
2253
+ * - If LoopResult.outcome === 'escalate', move to next tier
2254
+ * - If LoopResult.outcome === 'complete', build TaskResult and return
2255
+ *
2256
+ * Mode routing:
2257
+ * - mode === 'execute' → run the loop directly
2258
+ * - any other mode → delegate to getModeHandler() (plan/edit/debug)
2259
+ * getModeHandler still needs a ToolRegistry for legacy mode handlers.
2260
+ * We bridge via a thin ToolRegistry adapter wrapping ToolManager.
2261
+ *
2262
+ * Observability (from AgentConfig):
2263
+ * - onEvent → forwarded via ObservabilityMiddleware (agent:start/end, llm:*, tool:*, etc.)
2264
+ * - memory → context injected into system prompt via SystemPromptBuilder
2265
+ * - conversationHistory → injected into messages before first user turn
2266
+ * - filesRead/Modified/Created → tracked by ObservabilityMiddleware in run.meta
2267
+ * - workingDir → WorkspaceDiscovery enriches system prompt with repo map
2268
+ */
2269
+
2270
+ declare class SDKAgentRunner implements IAgentRunner {
2271
+ private readonly config;
2272
+ private readonly sdk;
2273
+ readonly agentId: string;
2274
+ private readonly abortController;
2275
+ private readonly injectedMessages;
2276
+ private readonly runMetricsEmitter;
2277
+ /** Accumulated token count for the current execution. Promoted to class field for budget cap access by spawnChildAgent. */
2278
+ private totalTokens;
2279
+ constructor(config: AgentConfig, sdk: AgentSDK);
2280
+ execute(task: string): Promise<TaskResult>;
2281
+ requestStop(): void;
2282
+ injectUserContext(message: string): void;
2283
+ private runExecuteMode;
2284
+ private executeWithTier;
2285
+ private extractTurnText;
2286
+ private loadConversationMessages;
2287
+ private buildLLMTools;
2288
+ /**
2289
+ * Spawn a child agent with budget cap, tool filtering, and structured results.
2290
+ *
2291
+ * Budget cap: childBudget = parentRemainingBudget × request.budgetFraction.
2292
+ * If parent has no budget (maxTokens=0), child also runs without budget.
2293
+ *
2294
+ * Tool filtering: request.allowedTools → childConfig.allowedTools → child's
2295
+ * ToolContext.allowedTools, which gates tool registration in createToolRegistry().
2296
+ */
2297
+ private spawnChildAgent;
2298
+ private runWithModeHandler;
2299
+ private withMetrics;
2300
+ private makeEmitContext;
2301
+ private recordTierEscalation;
2302
+ private emitRunKpis;
2303
+ private getAnalytics;
2304
+ private failResult;
2305
+ private getSessionFactsHint;
2306
+ private shouldInjectSessionFacts;
2307
+ private deriveDriftDomains;
2308
+ private evaluateQualityGate;
2309
+ }
2310
+
2311
+ /**
2312
+ * ToolExecutor — normalizers + guards + output processors pipeline around ToolManager.
2313
+ *
2314
+ * Execution order per tool call:
2315
+ * 1. Build ToolExecCtx
2316
+ * 2. normalize() — each InputNormalizer in order; transforms input
2317
+ * 3. validateInput() — each guard in order; 'reject' → error output, stop chain
2318
+ * 4. toolManager.execute(name, input)
2319
+ * 5. validateOutput() — each guard in order; 'sanitize' → replace output
2320
+ * 6. process() — each output processor in order
2321
+ * 7. Return ToolOutput
2322
+ */
2323
+
2324
+ declare class ToolExecutor {
2325
+ private readonly toolManager;
2326
+ private readonly guards;
2327
+ private readonly processors;
2328
+ private readonly normalizers;
2329
+ constructor(toolManager: ToolManager, guards: ReadonlyArray<ToolGuard>, processors: ReadonlyArray<OutputProcessor>, normalizers?: ReadonlyArray<InputNormalizer>);
2330
+ getToolManager(): ToolManager;
2331
+ execute(calls: ToolCallInput[], run: RunContext): Promise<ToolOutput[]>;
2332
+ private executeSingle;
2333
+ }
2334
+
2335
+ /**
2336
+ * LoopContextImpl — implements LoopContext from @kb-labs/agent-sdk.
2337
+ *
2338
+ * Provides the infrastructure primitives that LinearExecutionLoop calls:
2339
+ * - appendMessage() — the only way to mutate message history
2340
+ * - callLLM() — LLM call with beforeLLMCall/afterLLMCall middleware
2341
+ * - executeTools() — tool execution with beforeToolExec/afterToolExec middleware
2342
+ *
2343
+ * All business logic lives in the ExecutionLoop (LinearExecutionLoop).
2344
+ * LoopContextImpl is intentionally stateless between calls.
2345
+ */
2346
+
2347
+ declare class LoopContextImpl implements LoopContext$1 {
2348
+ private readonly messages;
2349
+ private readonly llm;
2350
+ private readonly pipeline;
2351
+ private readonly executor;
2352
+ private readonly onTokensConsumed;
2353
+ private readonly runEvaluator;
2354
+ readonly run: RunContext;
2355
+ constructor(run: RunContext, messages: LLMMessage[], // mutable backing array
2356
+ llm: ILLM, pipeline: MiddlewarePipeline, executor: ToolExecutor, onTokensConsumed: (delta: number) => void, runEvaluator: (run: RunContext, snapshot: IterationSnapshot) => Promise<RunEvaluation | null>);
2357
+ appendMessage(message: LLMMessage): void;
2358
+ beforeIteration(): Promise<ControlAction>;
2359
+ afterIteration(): Promise<void>;
2360
+ callLLM(): Promise<LLMCallResult>;
2361
+ executeTools(calls: ToolCallInput[]): Promise<ToolOutput[]>;
2362
+ /** Execute a single tool call with middleware hooks. */
2363
+ private _executeSingleTool;
2364
+ private static readonly OUTPUT_PERSIST_THRESHOLD;
2365
+ private static readonly OUTPUT_PREVIEW_LENGTH;
2366
+ /**
2367
+ * Tools whose output is NEVER persisted to disk — agent needs full content.
2368
+ * Inspired by Claude Code: Read tool has maxResultSizeChars=Infinity.
2369
+ * Size control for these tools happens at tool level (trimOutput, maxOutputChars),
2370
+ * not at loop level.
2371
+ */
2372
+ private static readonly PERSIST_EXEMPT_TOOLS;
2373
+ /**
2374
+ * If output exceeds threshold, persist to disk and return preview + path.
2375
+ * Otherwise return output as-is.
2376
+ */
2377
+ private _maybePersistOutput;
2378
+ evaluateRun(snapshot: IterationSnapshot): Promise<RunEvaluation | null>;
2379
+ }
2380
+
2381
+ /**
2382
+ * RunContext factory + ContextMeta implementation.
2383
+ *
2384
+ * RunContext is the top-level context shared across an entire agent run.
2385
+ * It is created once per tier attempt by SDKAgentRunner.
2386
+ *
2387
+ * Key invariant: messages is a readonly view of a mutable backing array.
2388
+ * The only legal mutation path is LoopContextImpl.appendMessage().
2389
+ */
2390
+
2391
+ declare class ContextMetaImpl implements ContextMeta {
2392
+ private readonly store;
2393
+ get<T>(namespace: string, key: string): T | undefined;
2394
+ set<T>(namespace: string, key: string, value: T): void;
2395
+ getNamespace(namespace: string): Record<string, unknown>;
2396
+ }
2397
+ interface CreateRunContextOptions {
2398
+ config: AgentConfig;
2399
+ tier: LLMTier;
2400
+ tools: LLMTool[];
2401
+ abortController: AbortController;
2402
+ requestId?: string;
2403
+ }
2404
+ /**
2405
+ * Creates a fresh RunContext for one tier attempt.
2406
+ * Returns the context AND the mutable backing array for LoopContextImpl.
2407
+ */
2408
+ declare function createRunContext(options: CreateRunContextOptions): {
2409
+ run: RunContext;
2410
+ messages: LLMMessage[];
2411
+ };
2412
+
2413
+ declare function createSessionMemoryBridge(workingDir: string, sessionId?: string): SessionMemoryBridge | undefined;
2414
+
2415
+ export { type AgentBehaviorPolicy, AgentRegistry, type AgentRunner, type AgentTypeDefinition, type AnalyticsCallbacks, AnalyticsMiddleware, BudgetMiddleware, type BudgetPolicy, type CapabilityType, type ChangeSnapshot, type ChangeTrackingConfig, ChangeTrackingMiddleware, ContextFilterMiddleware, type ContextFilterMwConfig, ContextMetaImpl, DebugModeHandler, type DelegationStrategy, EditModeHandler, type EmitContext, ExecuteModeHandler, ExecutionLoop, type ExecutionPhase, ExecutionStateMachine, type IterationContext, type KpiBaseline, type KpiBaselinePersister, type LedgerStep, type LegacySpawnRequest, type LegacySpawnResult, type LegacyToolRegistry, LinearExecutionLoop, type LoopContext, LoopContextImpl, type LoopLLMResponse, type LoopToolCall, type LoopToolResult, MiddlewarePipeline, type ModeHandler, type ModeHandlerFactory, type ModeRegistration, ModeRegistry, ObservabilityMiddleware, type OrchestratorConfig, ParallelExecutor, type ParallelExecutorConfig, type PipelineOptions, PlanArtifactWriter, PlanDocumentService, PlanExecutor, PlanGenerator, PlanModeHandler, PlanOutputValidator, type PlanValidationResult, PlanValidator, type ProgressCallbacks, ProgressMiddleware, type ReflectionCallbacks, ReflectionMiddleware, type RegisterOptions, type RegressionMetrics, type RubricScore, type RunKpiPayload, type RunMetrics, RunMetricsEmitter, SDKAgentRunner, type SearchSignalCallbacks, SearchSignalMiddleware, type SearchSignalMwState, SessionManager, type SpawnFn, SpecModeHandler, type SpecRubricScore, type SpecValidationResult, SpecValidator, type SpecValidatorIssue, type StepStatus, StopConditionEvaluator, type StopEvalContext, type StopEvalResponse, SubAgentOrchestrator, type SubAgentRequest, type SubAgentResult, SystemPromptBuilder, type SystemPromptInput, type TaskClassification, type TaskClassifierCallbacks, TaskClassifierMiddleware, type TaskIntent, TaskLedger, TaskMiddleware, type TaskMiddlewareConfig, type TierEscalation, type TodoSyncCallbacks, TodoSyncMiddleware, type TokenPartitionStrategy, ToolExecutor, ToolManager, type ToolManagerOptions, type ToolOutcome, type ToolOutcomeInput, type ToolPackRef, type ValidatorIssue, type WorkspaceDiscoveryResult, type WorkspaceRepoNode, bootstrapAgentSDK, clearProcessKpiBaselines, createAsyncTaskToolPack, createCoreToolPack, createDefaultAgentBehaviorPolicy, createKBLabsToolPack, createPlanResultMapper, createPlanRuntimeProfile, createRunContext, createSessionMemoryBridge, detectQualityRegression, discoverWorkspace, evaluateStopConditions, extractToolErrorCode, getFileChangeSummaries, getKpiBaselineKey, getModeHandler, getModeHandlerFromRegistry, globalAgentRegistry, loadProjectInstructions, mapToolToCapability, modeRegistry, updateKpiBaseline };