kimaki 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -912,9 +912,12 @@ async function handleOpencodeSession(prompt, thread, projectDirectory, originalM
912
912
  if (abortControllers.has(session.id)) {
913
913
  abortControllers.get(session.id)?.abort(new Error('new reply'));
914
914
  }
915
- const promptAbortController = new AbortController();
916
- abortControllers.set(session.id, promptAbortController);
917
- const eventsResult = await getClient().event.subscribe({});
915
+ const abortController = new AbortController();
916
+ // Store this controller for this session
917
+ abortControllers.set(session.id, abortController);
918
+ const eventsResult = await getClient().event.subscribe({
919
+ signal: abortController.signal,
920
+ });
918
921
  const events = eventsResult.stream;
919
922
  sessionLogger.log(`Subscribed to OpenCode events`);
920
923
  // Load existing part-message mappings from database
@@ -966,7 +969,7 @@ async function handleOpencodeSession(prompt, thread, projectDirectory, originalM
966
969
  // Outer-scoped interval for typing notifications. Only one at a time.
967
970
  let typingInterval = null;
968
971
  function startTyping(thread) {
969
- if (promptAbortController.signal.aborted) {
972
+ if (abortController.signal.aborted) {
970
973
  discordLogger.log(`Not starting typing, already aborted`);
971
974
  return () => { };
972
975
  }
@@ -988,8 +991,8 @@ async function handleOpencodeSession(prompt, thread, projectDirectory, originalM
988
991
  });
989
992
  }, 8000);
990
993
  // Only add listener if not already aborted
991
- if (!promptAbortController.signal.aborted) {
992
- promptAbortController.signal.addEventListener('abort', () => {
994
+ if (!abortController.signal.aborted) {
995
+ abortController.signal.addEventListener('abort', () => {
993
996
  if (typingInterval) {
994
997
  clearInterval(typingInterval);
995
998
  typingInterval = null;
@@ -1061,7 +1064,7 @@ async function handleOpencodeSession(prompt, thread, projectDirectory, originalM
1061
1064
  }
1062
1065
  // start typing in a moment, so that if the session finished, because step-finish is at the end of the message, we do not show typing status
1063
1066
  setTimeout(() => {
1064
- if (promptAbortController.signal.aborted)
1067
+ if (abortController.signal.aborted)
1065
1068
  return;
1066
1069
  stopTyping = startTyping(thread);
1067
1070
  }, 300);
@@ -1100,6 +1103,10 @@ async function handleOpencodeSession(prompt, thread, projectDirectory, originalM
1100
1103
  }
1101
1104
  }
1102
1105
  catch (e) {
1106
+ if (isAbortError(e, abortController.signal)) {
1107
+ sessionLogger.log('AbortController aborted event handling (normal exit)');
1108
+ return;
1109
+ }
1103
1110
  sessionLogger.error(`Unexpected error in event handling code`, e);
1104
1111
  throw e;
1105
1112
  }
@@ -1132,14 +1139,14 @@ async function handleOpencodeSession(prompt, thread, projectDirectory, originalM
1132
1139
  sessionLogger.log(`Stopped typing for session`);
1133
1140
  }
1134
1141
  // Only send duration message if request was not aborted or was aborted with 'finished' reason
1135
- if (!promptAbortController.signal.aborted ||
1136
- promptAbortController.signal.reason === 'finished') {
1142
+ if (!abortController.signal.aborted ||
1143
+ abortController.signal.reason === 'finished') {
1137
1144
  const sessionDuration = prettyMilliseconds(Date.now() - sessionStartTime);
1138
1145
  await sendThreadMessage(thread, `_Completed in ${sessionDuration}_`);
1139
1146
  sessionLogger.log(`DURATION: Session completed in ${sessionDuration}`);
1140
1147
  }
1141
1148
  else {
1142
- sessionLogger.log(`Session was aborted (reason: ${promptAbortController.signal.reason}), skipping duration message`);
1149
+ sessionLogger.log(`Session was aborted (reason: ${abortController.signal.reason}), skipping duration message`);
1143
1150
  }
1144
1151
  }
1145
1152
  };
@@ -1152,9 +1159,9 @@ async function handleOpencodeSession(prompt, thread, projectDirectory, originalM
1152
1159
  body: {
1153
1160
  parts: [{ type: 'text', text: prompt }],
1154
1161
  },
1155
- signal: promptAbortController.signal,
1162
+ signal: abortController.signal,
1156
1163
  });
1157
- promptAbortController.abort(new Error('finished'));
1164
+ abortController.abort(new Error('finished'));
1158
1165
  sessionLogger.log(`Successfully sent prompt, got response`);
1159
1166
  abortControllers.delete(session.id);
1160
1167
  // Update reaction to success
@@ -1172,8 +1179,8 @@ async function handleOpencodeSession(prompt, thread, projectDirectory, originalM
1172
1179
  }
1173
1180
  catch (error) {
1174
1181
  sessionLogger.error(`ERROR: Failed to send prompt:`, error);
1175
- if (!isAbortError(error, promptAbortController.signal)) {
1176
- promptAbortController.abort(new Error('error'));
1182
+ if (!isAbortError(error, abortController.signal)) {
1183
+ abortController.abort(new Error('error'));
1177
1184
  if (originalMessage) {
1178
1185
  try {
1179
1186
  await originalMessage.reactions.removeAll();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "kimaki",
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
- "version": "0.4.1",
5
+ "version": "0.4.2",
6
6
  "scripts": {
7
7
  "dev": "pnpm tsc && tsx --env-file .env src/cli.ts",
8
8
  "prepublishOnly": "pnpm tsc",
package/src/discordBot.ts CHANGED
@@ -1175,10 +1175,13 @@ async function handleOpencodeSession(
1175
1175
  if (abortControllers.has(session.id)) {
1176
1176
  abortControllers.get(session.id)?.abort(new Error('new reply'))
1177
1177
  }
1178
- const promptAbortController = new AbortController()
1179
- abortControllers.set(session.id, promptAbortController)
1178
+ const abortController = new AbortController()
1179
+ // Store this controller for this session
1180
+ abortControllers.set(session.id, abortController)
1180
1181
 
1181
- const eventsResult = await getClient().event.subscribe({})
1182
+ const eventsResult = await getClient().event.subscribe({
1183
+ signal: abortController.signal,
1184
+ })
1182
1185
  const events = eventsResult.stream
1183
1186
  sessionLogger.log(`Subscribed to OpenCode events`)
1184
1187
 
@@ -1250,7 +1253,7 @@ async function handleOpencodeSession(
1250
1253
  let typingInterval: NodeJS.Timeout | null = null
1251
1254
 
1252
1255
  function startTyping(thread: ThreadChannel): () => void {
1253
- if (promptAbortController.signal.aborted) {
1256
+ if (abortController.signal.aborted) {
1254
1257
  discordLogger.log(`Not starting typing, already aborted`)
1255
1258
  return () => {}
1256
1259
  }
@@ -1276,8 +1279,8 @@ async function handleOpencodeSession(
1276
1279
  }, 8000)
1277
1280
 
1278
1281
  // Only add listener if not already aborted
1279
- if (!promptAbortController.signal.aborted) {
1280
- promptAbortController.signal.addEventListener(
1282
+ if (!abortController.signal.aborted) {
1283
+ abortController.signal.addEventListener(
1281
1284
  'abort',
1282
1285
  () => {
1283
1286
  if (typingInterval) {
@@ -1375,7 +1378,7 @@ async function handleOpencodeSession(
1375
1378
  }
1376
1379
  // start typing in a moment, so that if the session finished, because step-finish is at the end of the message, we do not show typing status
1377
1380
  setTimeout(() => {
1378
- if (promptAbortController.signal.aborted) return
1381
+ if (abortController.signal.aborted) return
1379
1382
  stopTyping = startTyping(thread)
1380
1383
  }, 300)
1381
1384
  }
@@ -1415,6 +1418,12 @@ async function handleOpencodeSession(
1415
1418
  }
1416
1419
  }
1417
1420
  } catch (e) {
1421
+ if (isAbortError(e, abortController.signal)) {
1422
+ sessionLogger.log(
1423
+ 'AbortController aborted event handling (normal exit)',
1424
+ )
1425
+ return
1426
+ }
1418
1427
  sessionLogger.error(`Unexpected error in event handling code`, e)
1419
1428
  throw e
1420
1429
  } finally {
@@ -1454,8 +1463,8 @@ async function handleOpencodeSession(
1454
1463
 
1455
1464
  // Only send duration message if request was not aborted or was aborted with 'finished' reason
1456
1465
  if (
1457
- !promptAbortController.signal.aborted ||
1458
- promptAbortController.signal.reason === 'finished'
1466
+ !abortController.signal.aborted ||
1467
+ abortController.signal.reason === 'finished'
1459
1468
  ) {
1460
1469
  const sessionDuration = prettyMilliseconds(
1461
1470
  Date.now() - sessionStartTime,
@@ -1464,7 +1473,7 @@ async function handleOpencodeSession(
1464
1473
  sessionLogger.log(`DURATION: Session completed in ${sessionDuration}`)
1465
1474
  } else {
1466
1475
  sessionLogger.log(
1467
- `Session was aborted (reason: ${promptAbortController.signal.reason}), skipping duration message`,
1476
+ `Session was aborted (reason: ${abortController.signal.reason}), skipping duration message`,
1468
1477
  )
1469
1478
  }
1470
1479
  }
@@ -1483,9 +1492,9 @@ async function handleOpencodeSession(
1483
1492
  body: {
1484
1493
  parts: [{ type: 'text', text: prompt }],
1485
1494
  },
1486
- signal: promptAbortController.signal,
1495
+ signal: abortController.signal,
1487
1496
  })
1488
- promptAbortController.abort(new Error('finished'))
1497
+ abortController.abort(new Error('finished'))
1489
1498
 
1490
1499
  sessionLogger.log(`Successfully sent prompt, got response`)
1491
1500
 
@@ -1506,8 +1515,8 @@ async function handleOpencodeSession(
1506
1515
  } catch (error) {
1507
1516
  sessionLogger.error(`ERROR: Failed to send prompt:`, error)
1508
1517
 
1509
- if (!isAbortError(error, promptAbortController.signal)) {
1510
- promptAbortController.abort(new Error('error'))
1518
+ if (!isAbortError(error, abortController.signal)) {
1519
+ abortController.abort(new Error('error'))
1511
1520
 
1512
1521
  if (originalMessage) {
1513
1522
  try {