(
request: PartListUnion,
signal: AbortSignal,
prompt_id: string,
turns: number = MAX_TURNS,
originalModel?: string,
)
| 446 | } |
| 447 | |
| 448 | async *sendMessageStream( |
| 449 | request: PartListUnion, |
| 450 | signal: AbortSignal, |
| 451 | prompt_id: string, |
| 452 | turns: number = MAX_TURNS, |
| 453 | originalModel?: string, |
| 454 | ): AsyncGenerator<ServerAnusStreamEvent, Turn> { |
| 455 | if (this.lastPromptId !== prompt_id) { |
| 456 | this.loopDetector.reset(prompt_id); |
| 457 | this.lastPromptId = prompt_id; |
| 458 | } |
| 459 | this.sessionTurnCount++; |
| 460 | if ( |
| 461 | this.config.getMaxSessionTurns() > 0 && |
| 462 | this.sessionTurnCount > this.config.getMaxSessionTurns() |
| 463 | ) { |
| 464 | yield { type: AnusEventType.MaxSessionTurns }; |
| 465 | return new Turn(this.getChat(), prompt_id); |
| 466 | } |
| 467 | // Ensure turns never exceeds MAX_TURNS to prevent infinite loops |
| 468 | const boundedTurns = Math.min(turns, MAX_TURNS); |
| 469 | if (!boundedTurns) { |
| 470 | return new Turn(this.getChat(), prompt_id); |
| 471 | } |
| 472 | |
| 473 | // Track the original model from the first call to detect model switching |
| 474 | const initialModel = originalModel || this.config.getModel(); |
| 475 | |
| 476 | const compressed = await this.tryCompressChat(prompt_id); |
| 477 | |
| 478 | if (compressed) { |
| 479 | yield { type: AnusEventType.ChatCompressed, value: compressed }; |
| 480 | } |
| 481 | |
| 482 | // Prevent context updates from being sent while a tool call is |
| 483 | // waiting for a response. The Anus API requires that a functionResponse |
| 484 | // part from the user immediately follows a functionCall part from the model |
| 485 | // in the conversation history . The IDE context is not discarded; it will |
| 486 | // be included in the next regular message sent to the model. |
| 487 | const history = this.getHistory(); |
| 488 | const lastMessage = |
| 489 | history.length > 0 ? history[history.length - 1] : undefined; |
| 490 | const hasPendingToolCall = |
| 491 | !!lastMessage && |
| 492 | lastMessage.role === 'model' && |
| 493 | (lastMessage.parts?.some((p) => 'functionCall' in p) || false); |
| 494 | |
| 495 | if (this.config.getIdeMode() && !hasPendingToolCall) { |
| 496 | const { contextParts, newIdeContext } = this.getIdeContextParts( |
| 497 | this.forceFullIdeContext || history.length === 0, |
| 498 | ); |
| 499 | if (contextParts.length > 0) { |
| 500 | this.getChat().addHistory({ |
| 501 | role: 'user', |
| 502 | parts: [{ text: contextParts.join('\n') }], |
| 503 | }); |
| 504 | } |
| 505 | this.lastSentIdeContext = newIdeContext; |
no test coverage detected