claude-flow
Version:
Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration
138 lines • 6.49 kB
TypeScript
/**
* GAIA Convergence Layer — deterministic finalization for saturated agent loops.
*
* Detects three failure modes that cause empty FINAL_ANSWER extraction:
* 1. max_turns hit without a final_answer call
* 2. Loop detected (same tool + same args called 3× in a 5-turn window)
* 3. Token budget exceeded (>120k tokens of conversation context)
*
* On detection, runs a FORCED COMMIT phase:
* - Injects a stripped-down summary prompt: "Based on all observations, answer with
* FINAL_ANSWER: X. Do NOT explore further."
* - Makes 1 final API call with strict instruction and no tools.
* - If still no FINAL_ANSWER in the response, runs the Stage 1 extraction cascade
* against ALL prior assistant messages (last to first), returns first non-empty hit.
* - If still empty: returns null but logs the failure mode.
*
* This is NOT a new cognition layer — it is a convergence controller.
* It STRIPS information rather than adding it: one final chance to commit,
* no tools available, prior context summarized rather than appended.
*
* Architecture principle (iter 60 post-mortem):
* "More information can reduce agent reliability. Past a certain point —
* retrieval depth, context size, browsing breadth, tool diversity — information
* increases trajectory entropy. The system becomes less likely to finalize coherently."
* This layer is the entropy-reducer.
*
* Refs: #2156, iter 60 (19/25 empty FINAL_ANSWER failures), iter 62
*/
/** Token threshold (sum of input tokens across all turns) that triggers overflow detection. */
export declare const TOKEN_OVERFLOW_THRESHOLD = 120000;
/** Number of repeated identical tool+args calls in a window that signals a loop. */
export declare const LOOP_REPEAT_THRESHOLD = 3;
/** Sliding window size (turns) for loop detection. */
export declare const LOOP_WINDOW_SIZE = 5;
/** Tracks state that the convergence layer needs to evaluate triggers. */
export interface ConvergenceState {
/** How many agent turns have elapsed so far. */
turnCount: number;
/** Sum of input tokens across all turns (used for overflow detection). */
totalTokens: number;
/** Ordered log of tool calls for loop detection. */
toolCalls: Array<{
name: string;
argsHash: string;
turn: number;
}>;
/** Set by checkConvergenceTriggers when a failure mode is detected. */
detectedFailureMode: 'max_turns' | 'loop' | 'token_overflow' | null;
}
/** Result of a forced-commit attempt. */
export interface ForceCommitResult {
/** The extracted answer, or null if even forced commit could not extract one. */
answer: string | null;
/** True when the answer was recovered from prior message history rather than
* from the forced-commit API call. */
usedFallback: boolean;
/** The failure mode that triggered this forced commit. */
triggerMode: ConvergenceState['detectedFailureMode'];
}
/**
* Produce a stable hash of a tool call's name + args for loop detection.
*
* Uses SHA-256 truncated to 16 hex chars — collision probability is
* negligible for the small call volumes in a single agent run.
*
* The hash is deterministic: same toolName + same args always → same hash.
* Different args always → different hash (within SHA-256 collision bounds).
*/
export declare function argsHash(toolName: string, args: object): string;
/**
* Evaluate the current ConvergenceState and return the first failure mode
* detected, or null if no trigger has fired.
*
* Evaluation order:
* 1. max_turns — turnCount >= maxTurns
* 2. token_overflow — totalTokens >= TOKEN_OVERFLOW_THRESHOLD
* 3. loop — same tool+argsHash appears >= LOOP_REPEAT_THRESHOLD times
* in the last LOOP_WINDOW_SIZE entries of toolCalls
*
* Only the FIRST matching trigger is returned (stops at first detection).
* The caller is responsible for setting state.detectedFailureMode.
*/
export declare function checkConvergenceTriggers(state: ConvergenceState, maxTurns: number): ConvergenceState['detectedFailureMode'];
/**
* Run Stage 1 extraction (FINAL_ANSWER: pattern) against a raw text string.
* Returns the matched answer or null.
*/
export declare function extractFinalAnswerFromText(text: string): string | null;
/**
* Scan prior assistant messages from last to first, looking for FINAL_ANSWER.
*
* This is the fallback path when the forced-commit API call still does not
* produce a FINAL_ANSWER. We search backwards because the most recent
* assistant message is most likely to contain the best answer.
*/
export declare function extractFromPriorMessages(messages: Array<{
role: string;
content: string | unknown;
}>): string | null;
/**
* Run the forced-commit phase for a saturated agent loop.
*
* Steps:
* 1. Append a stripped directive prompt to the message history.
* 2. Call the model once with NO tools — forces text-only response.
* 3. If FINAL_ANSWER found in the response → return it.
* 4. If not → scan prior assistant messages last-to-first for FINAL_ANSWER.
* 5. If still empty → return { answer: null, usedFallback: false }.
*
* The callModel callback must return the raw text response (assistant turn
* text blocks concatenated). It should be called WITHOUT tool definitions
* so the model cannot call tools in this turn.
*
* @param messages Current conversation history (mutated: 1 user turn appended)
* @param callModel Callback that makes a single API call and returns the response text
* @param triggerMode The failure mode that triggered this forced commit
*/
export declare function forceCommit(messages: Array<{
role: string;
content: string | unknown;
}>, callModel: (messages: Array<{
role: string;
content: string | unknown;
}>) => Promise<string>, triggerMode?: ConvergenceState['detectedFailureMode']): Promise<ForceCommitResult>;
/** Create a new ConvergenceState with zero counters. */
export declare function createConvergenceState(): ConvergenceState;
/**
* Update ConvergenceState after each agent turn completes.
*
* @param state Mutable state object (modified in place)
* @param inputTokens Tokens consumed in this turn
* @param toolCallsThisTurn Tool calls made in this turn (name + args)
*/
export declare function recordTurn(state: ConvergenceState, inputTokens: number, toolCallsThisTurn: Array<{
name: string;
args: object;
}>): void;
//# sourceMappingURL=gaia-convergence.d.ts.map