feat(agent): added agent tool-call cap enforcement to stream loop
- Added `maxToolCallsPerTurn` support to `AgentOptions` and `AgentLoopConfig`, with Agent getter/setter and serialized state wiring. - Implemented stream-loop cap handling by normalizing bad values and halting after `toolcall_end` reaches the limit. - Added `ANTHROPIC_TOOL_CALL_BATCH_CAP`=8 and wired session cap sync on init, model changes, and restore. - Added tests that truncated a 10-call stream to 8 tool calls, and verified non-Claude models resolve no cap.
This commit is contained in:
@@ -453,6 +453,15 @@ function formatRetryFallbackBaseSelector(selector: RetryFallbackSelector): strin
|
||||
}
|
||||
|
||||
const IRC_REPLY_MAX_BYTES = 4096;
|
||||
export const ANTHROPIC_TOOL_CALL_BATCH_CAP = 4;
|
||||
const CLAUDE_OPUS_4_8_MODEL_ID = /(?:^|[./_-])claude-opus-4[.-]8\b/i;
|
||||
|
||||
export function resolveToolCallBatchCapForModel(model: Model | undefined): number | undefined {
|
||||
if (!model) return undefined;
|
||||
return model.provider === "anthropic" && CLAUDE_OPUS_4_8_MODEL_ID.test(model.id)
|
||||
? ANTHROPIC_TOOL_CALL_BATCH_CAP
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse degenerate IRC ephemeral replies before they hit the relay.
|
||||
@@ -993,6 +1002,10 @@ export class AgentSession {
|
||||
this.#flushPendingAgentEnd();
|
||||
}
|
||||
|
||||
#syncToolCallBatchCap(model: Model | undefined = this.model): void {
|
||||
this.agent.maxToolCallsPerTurn = resolveToolCallBatchCapForModel(model);
|
||||
}
|
||||
|
||||
#flushPendingAgentEnd(): void {
|
||||
const pending = this.#pendingAgentEndEmit;
|
||||
if (!pending) return;
|
||||
@@ -1097,6 +1110,7 @@ export class AgentSession {
|
||||
this.#agentId = config.agentId;
|
||||
this.#agentRegistry = config.agentRegistry;
|
||||
this.#providerSessionId = config.providerSessionId;
|
||||
this.#syncToolCallBatchCap();
|
||||
this.agent.setAssistantMessageEventInterceptor((message, assistantMessageEvent) => {
|
||||
const event: AgentEvent = {
|
||||
type: "message_update",
|
||||
@@ -6162,6 +6176,7 @@ export class AgentSession {
|
||||
this.#closeProviderSessionsForModelSwitch(currentModel, model);
|
||||
}
|
||||
this.agent.setModel(model);
|
||||
this.#syncToolCallBatchCap(model);
|
||||
|
||||
// Re-evaluate append-only context mode — provider or setting may have changed
|
||||
this.#syncAppendOnlyContext(model);
|
||||
@@ -8214,6 +8229,7 @@ export class AgentSession {
|
||||
this.#setModelWithProviderSessionReset(match);
|
||||
} else {
|
||||
this.agent.setModel(match);
|
||||
this.#syncToolCallBatchCap(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8272,6 +8288,9 @@ export class AgentSession {
|
||||
this.#scheduledHiddenNextTurnGeneration = previousScheduledHiddenNextTurnGeneration;
|
||||
if (previousModel) {
|
||||
this.agent.setModel(previousModel);
|
||||
this.#syncToolCallBatchCap(previousModel);
|
||||
} else {
|
||||
this.#syncToolCallBatchCap(undefined);
|
||||
}
|
||||
this.#thinkingLevel = previousThinkingLevel;
|
||||
this.agent.setThinkingLevel(toReasoningEffort(previousThinkingLevel));
|
||||
|
||||
Reference in New Issue
Block a user