From 37bab0039d17b5074be87089826c92336b7bc512 Mon Sep 17 00:00:00 2001 From: zbigniew sobiecki Date: Sun, 18 Jan 2026 14:37:39 +0100 Subject: [PATCH] feat(gadgets): add Finish gadget for early agent termination Agents that complete work before maxIterations had no way to signal completion, causing them to loop saying "I have completed the task" until hitting the iteration limit. The Finish gadget allows agents to cleanly end their session early by throwing TaskCompletionSignal. Co-Authored-By: Claude Opus 4.5 --- src/agents/base.ts | 3 +++ src/gadgets/Finish.ts | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/gadgets/Finish.ts diff --git a/src/agents/base.ts b/src/agents/base.ts index 1e6fd1f8..3b923e31 100644 --- a/src/agents/base.ts +++ b/src/agents/base.ts @@ -10,6 +10,7 @@ import { getIterationTrailingMessage } from '../config/hintConfig.js'; import { getRateLimitForModel } from '../config/rateLimits.js'; import { getRetryConfig } from '../config/retryConfig.js'; import { EditFile } from '../gadgets/EditFile.js'; +import { Finish } from '../gadgets/Finish.js'; import { ListDirectory } from '../gadgets/ListDirectory.js'; import { ReadFile } from '../gadgets/ReadFile.js'; import { Sleep } from '../gadgets/Sleep.js'; @@ -303,6 +304,8 @@ function createAgentBuilderWithGadgets( // new GetMyRecentActivity(), // Temporarily disabled new AddChecklistToCard(), new UpdateChecklistItem(), + // Session control + new Finish(), ]; const allGadgets = auEnabled ? [...baseGadgets, auList, auRead] : baseGadgets; diff --git a/src/gadgets/Finish.ts b/src/gadgets/Finish.ts new file mode 100644 index 00000000..b32a6bca --- /dev/null +++ b/src/gadgets/Finish.ts @@ -0,0 +1,21 @@ +import { Gadget, TaskCompletionSignal, z } from 'llmist'; + +export class Finish extends Gadget({ + name: 'Finish', + description: + 'Call this gadget when you have completed all tasks and want to end the session. This should be your final gadget call.', + schema: z.object({ + comment: z.string().min(1).describe('A brief summary of what was accomplished'), + }), + examples: [ + { + params: { comment: 'Created PR with all requested changes and tests passing' }, + output: 'Session ended: Created PR with all requested changes and tests passing', + comment: 'End session after completing all work', + }, + ], +}) { + override execute(params: this['params']): never { + throw new TaskCompletionSignal(params.comment); + } +}