Skip to content

Commit 67d72b6

Browse files
committed
add ctrl-c to cancel agent response
1 parent 7d962f7 commit 67d72b6

File tree

6 files changed

+181
-142
lines changed

6 files changed

+181
-142
lines changed

cli/src/hooks/use-keyboard-handlers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ export const useKeyboardHandlers = ({
4949
if (abortControllerRef.current) {
5050
abortControllerRef.current.abort()
5151
}
52+
53+
return
5254
}
5355

5456
if (isCtrlC) {

cli/src/hooks/use-send-message.ts

Lines changed: 71 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,46 @@ export const useSendMessage = ({
616616

617617
const abortController = new AbortController()
618618
abortControllerRef.current = abortController
619+
abortController.signal.addEventListener('abort', () => {
620+
setIsStreaming(false)
621+
setCanProcessQueue(true)
622+
updateChainInProgress(false)
623+
setIsWaitingForResponse(false)
624+
timerController.stop('aborted')
625+
626+
applyMessageUpdate((prev) =>
627+
prev.map((msg) => {
628+
if (msg.id !== aiMessageId) {
629+
return msg
630+
}
631+
632+
const blocks: ContentBlock[] = msg.blocks ? [...msg.blocks] : []
633+
const lastBlock = blocks[blocks.length - 1]
634+
635+
if (lastBlock && lastBlock.type === 'text') {
636+
const interruptedBlock: ContentBlock = {
637+
type: 'text',
638+
content: `${lastBlock.content}\n\n[response interrupted]`,
639+
}
640+
return {
641+
...msg,
642+
blocks: [...blocks.slice(0, -1), interruptedBlock],
643+
isComplete: true,
644+
}
645+
}
646+
647+
const interruptionNotice: ContentBlock = {
648+
type: 'text',
649+
content: '[response interrupted]',
650+
}
651+
return {
652+
...msg,
653+
blocks: [...blocks, interruptionNotice],
654+
isComplete: true,
655+
}
656+
}),
657+
)
658+
})
619659

620660
try {
621661
// Load local agent definitions from .agents directory
@@ -633,7 +673,7 @@ export const useSendMessage = ({
633673
: agentMode === 'MAX'
634674
? 'base2-max'
635675
: 'base2-plan'
636-
const result = await client.run({
676+
const runState = await client.run({
637677
logger,
638678
agent: selectedAgentDefinition ?? agentId ?? fallbackAgent,
639679
prompt: content,
@@ -1056,7 +1096,8 @@ export const useSendMessage = ({
10561096
}
10571097

10581098
if (event.type === 'tool_call' && event.toolCallId) {
1059-
const { toolCallId, toolName, input, agentId, includeToolCall } = event
1099+
const { toolCallId, toolName, input, agentId, includeToolCall } =
1100+
event
10601101

10611102
if (toolName === 'spawn_agents' && input?.agents) {
10621103
const agents = Array.isArray(input.agents) ? input.agents : []
@@ -1136,7 +1177,9 @@ export const useSendMessage = ({
11361177
toolName,
11371178
input,
11381179
agentId,
1139-
...(includeToolCall !== undefined && { includeToolCall }),
1180+
...(includeToolCall !== undefined && {
1181+
includeToolCall,
1182+
}),
11401183
}
11411184

11421185
return {
@@ -1306,6 +1349,14 @@ export const useSendMessage = ({
13061349
},
13071350
})
13081351

1352+
if (runState.output.type === 'error') {
1353+
logger.warn(
1354+
{ errorMessage: runState.output.message },
1355+
'Agent run failed',
1356+
)
1357+
return
1358+
}
1359+
13091360
setIsStreaming(false)
13101361
setCanProcessQueue(true)
13111362
updateChainInProgress(false)
@@ -1316,10 +1367,6 @@ export const useSendMessage = ({
13161367
setHasReceivedPlanResponse(true)
13171368
}
13181369

1319-
if ((result as any)?.credits !== undefined) {
1320-
actualCredits = (result as any).credits
1321-
}
1322-
13231370
const elapsedMs = timerResult?.elapsedMs ?? 0
13241371
const elapsedSeconds = Math.floor(elapsedMs / 1000)
13251372
const completionTime =
@@ -1340,10 +1387,8 @@ export const useSendMessage = ({
13401387
),
13411388
)
13421389

1343-
previousRunStateRef.current = result
1390+
previousRunStateRef.current = runState
13441391
} catch (error) {
1345-
const isAborted = error instanceof Error && error.name === 'AbortError'
1346-
13471392
logger.error(
13481393
{ error: getErrorObject(error) },
13491394
'SDK client.run() failed',
@@ -1352,61 +1397,26 @@ export const useSendMessage = ({
13521397
setCanProcessQueue(true)
13531398
updateChainInProgress(false)
13541399
setIsWaitingForResponse(false)
1355-
timerController.stop(isAborted ? 'aborted' : 'error')
1356-
1357-
if (isAborted) {
1358-
applyMessageUpdate((prev) =>
1359-
prev.map((msg) => {
1360-
if (msg.id !== aiMessageId) {
1361-
return msg
1362-
}
1400+
timerController.stop('error')
13631401

1364-
const blocks: ContentBlock[] = msg.blocks ? [...msg.blocks] : []
1365-
const lastBlock = blocks[blocks.length - 1]
1366-
1367-
if (lastBlock && lastBlock.type === 'text') {
1368-
const interruptedBlock: ContentBlock = {
1369-
type: 'text',
1370-
content: `${lastBlock.content}\n\n[response interrupted]`,
1371-
}
1372-
return {
1402+
const errorMessage =
1403+
error instanceof Error ? error.message : 'Unknown error occurred'
1404+
applyMessageUpdate((prev) =>
1405+
prev.map((msg) =>
1406+
msg.id === aiMessageId
1407+
? {
13731408
...msg,
1374-
blocks: [...blocks.slice(0, -1), interruptedBlock],
1375-
isComplete: true,
1409+
content: msg.content + `\n\n**Error:** ${errorMessage}`,
13761410
}
1377-
}
1378-
1379-
const interruptionNotice: ContentBlock = {
1380-
type: 'text',
1381-
content: '[response interrupted]',
1382-
}
1383-
return {
1384-
...msg,
1385-
blocks: [...blocks, interruptionNotice],
1386-
isComplete: true,
1387-
}
1388-
}),
1389-
)
1390-
} else {
1391-
const errorMessage =
1392-
error instanceof Error ? error.message : 'Unknown error occurred'
1393-
applyMessageUpdate((prev) =>
1394-
prev.map((msg) =>
1395-
msg.id === aiMessageId
1396-
? {
1397-
...msg,
1398-
content: msg.content + `\n\n**Error:** ${errorMessage}`,
1399-
}
1400-
: msg,
1401-
),
1402-
)
1411+
: msg,
1412+
),
1413+
)
14031414

1404-
applyMessageUpdate((prev) =>
1405-
prev.map((msg) =>
1406-
msg.id === aiMessageId ? { ...msg, isComplete: true } : msg,
1407-
),
1408-
)
1409-
}
1415+
applyMessageUpdate((prev) =>
1416+
prev.map((msg) =>
1417+
msg.id === aiMessageId ? { ...msg, isComplete: true } : msg,
1418+
),
1419+
)
14101420
}
14111421
},
14121422
[

sdk/README.md

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async function main() {
2828
})
2929

3030
// First run
31-
const runOrError1 = await client.run({
31+
const runState1 = await client.run({
3232
// The agent id. Any agent on the store (https://codebuff.com/store)
3333
agent: 'codebuff/base@0.0.16',
3434
prompt: 'Create a simple calculator class',
@@ -37,23 +37,16 @@ async function main() {
3737
console.log('Codebuff Event', JSON.stringify(event))
3838
},
3939
})
40-
if (!runOrError1.success) {
41-
throw new Error('Run failed' + runOrError1.error.message)
42-
}
43-
const run1 = runOrError1.value
4440

4541
// Continue the same session with a follow-up
4642
const runOrError2 = await client.run({
4743
agent: 'codebuff/base@0.0.16',
4844
prompt: 'Add unit tests for the calculator',
49-
previousRun: run1, // <-- this is where your next run differs from the previous run
45+
previousRun: runState1, // <-- this is where your next run differs from the previous run
5046
handleEvent: (event) => {
5147
console.log('Codebuff Event', JSON.stringify(event))
5248
},
5349
})
54-
if (!runOrError2.success) {
55-
throw new Error('Run failed: ' + runOrError2.error.message)
56-
}
5750
}
5851

5952
main()
@@ -116,7 +109,7 @@ async function main() {
116109
},
117110
})
118111

119-
const runOrError = await client.run({
112+
const { output } = await client.run({
120113
// Run a custom agent by id. Must match an id in the agentDefinitions field below.
121114
agent: 'my-custom-agent',
122115
prompt: "Today I'm feeling very happy!",
@@ -130,10 +123,6 @@ async function main() {
130123
console.log('Codebuff Event', JSON.stringify(event))
131124
},
132125
})
133-
if (!runOrError.success) {
134-
throw new Error('Run failed: ' + runOrError.error.message)
135-
}
136-
const { output } = runOrError.value
137126

138127
if (output.type === 'error') {
139128
console.error(`The run failed:\n${output.message}`)

sdk/examples/readme-example-1.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ async function main() {
99
})
1010

1111
// First run
12-
const runOrError1 = await client.run({
12+
const runState1 = await client.run({
1313
// The agent id. Any agent on the store (https://codebuff.com/store)
1414
agent: 'codebuff/base@0.0.16',
1515
prompt: 'Create a simple calculator class',
@@ -18,23 +18,16 @@ async function main() {
1818
console.log('Codebuff Event', JSON.stringify(event))
1919
},
2020
})
21-
if (!runOrError1.success) {
22-
throw new Error('Run failed' + runOrError1.error.message)
23-
}
24-
const run1 = runOrError1.value
2521

2622
// Continue the same session with a follow-up
2723
const runOrError2 = await client.run({
2824
agent: 'codebuff/base@0.0.16',
2925
prompt: 'Add unit tests for the calculator',
30-
previousRun: run1, // <-- this is where your next run differs from the previous run
26+
previousRun: runState1, // <-- this is where your next run differs from the previous run
3127
handleEvent: (event) => {
3228
console.log('Codebuff Event', JSON.stringify(event))
3329
},
3430
})
35-
if (!runOrError2.success) {
36-
throw new Error('Run failed: ' + runOrError2.error.message)
37-
}
3831
}
3932

4033
main()

sdk/examples/readme-example-2.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ async function main() {
5050
},
5151
})
5252

53-
const runOrError = await client.run({
53+
const { output } = await client.run({
5454
// Run a custom agent by id. Must match an id in the agentDefinitions field below.
5555
agent: 'my-custom-agent',
5656
prompt: "Today I'm feeling very happy!",
@@ -64,10 +64,6 @@ async function main() {
6464
console.log('Codebuff Event', JSON.stringify(event))
6565
},
6666
})
67-
if (!runOrError.success) {
68-
throw new Error('Run failed: ' + runOrError.error.message)
69-
}
70-
const { output } = runOrError.value
7167

7268
if (output.type === 'error') {
7369
console.error(`The run failed:\n${output.message}`)

0 commit comments

Comments
 (0)