@ian2018cs/agenthub 0.1.62 → 0.1.63

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.
package/dist/index.html CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  <!-- Prevent zoom on iOS -->
27
27
  <meta name="format-detection" content="telephone=no" />
28
- <script type="module" crossorigin src="/assets/index-DWxvF29L.js"></script>
28
+ <script type="module" crossorigin src="/assets/index-Bih5jXn3.js"></script>
29
29
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BeVl62c0.js">
30
30
  <link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-C_VWDoZS.js">
31
31
  <link rel="modulepreload" crossorigin href="/assets/vendor-utils-00TdZexr.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ian2018cs/agenthub",
3
- "version": "0.1.62",
3
+ "version": "0.1.63",
4
4
  "description": "A web-based UI for AI Agents",
5
5
  "type": "module",
6
6
  "main": "server/index.js",
@@ -232,13 +232,14 @@ function mapCliOptionsToSDK(options = {}) {
232
232
  * @param {Array<string>} tempImagePaths - Temp image file paths for cleanup
233
233
  * @param {string} tempDir - Temp directory for cleanup
234
234
  */
235
- function addSession(sessionId, queryInstance, tempImagePaths = [], tempDir = null) {
235
+ function addSession(sessionId, queryInstance, tempImagePaths = [], tempDir = null, abortController = null) {
236
236
  activeSessions.set(sessionId, {
237
237
  instance: queryInstance,
238
238
  startTime: Date.now(),
239
239
  status: 'active',
240
240
  tempImagePaths,
241
- tempDir
241
+ tempDir,
242
+ abortController
242
243
  });
243
244
  }
244
245
 
@@ -511,6 +512,10 @@ async function queryClaudeSDK(command, options = {}, ws) {
511
512
  // Map CLI options to SDK format
512
513
  const sdkOptions = mapCliOptionsToSDK(options);
513
514
 
515
+ // Create AbortController for session cancellation
516
+ const abortController = new AbortController();
517
+ sdkOptions.abortController = abortController;
518
+
514
519
  // Load MCP configuration
515
520
  const mcpServers = await loadMcpConfig(options.cwd, userUuid);
516
521
  if (mcpServers) {
@@ -651,7 +656,7 @@ async function queryClaudeSDK(command, options = {}, ws) {
651
656
 
652
657
  // Track the query instance for abort capability
653
658
  if (capturedSessionId) {
654
- addSession(capturedSessionId, queryInstance, tempImagePaths, tempDir);
659
+ addSession(capturedSessionId, queryInstance, tempImagePaths, tempDir, abortController);
655
660
  }
656
661
 
657
662
  // Process streaming messages
@@ -661,7 +666,7 @@ async function queryClaudeSDK(command, options = {}, ws) {
661
666
  if (message.session_id && !capturedSessionId) {
662
667
 
663
668
  capturedSessionId = message.session_id;
664
- addSession(capturedSessionId, queryInstance, tempImagePaths, tempDir);
669
+ addSession(capturedSessionId, queryInstance, tempImagePaths, tempDir, abortController);
665
670
 
666
671
  // Set session ID on writer
667
672
  if (ws.setSessionId && typeof ws.setSessionId === 'function') {
@@ -775,17 +780,33 @@ async function queryClaudeSDK(command, options = {}, ws) {
775
780
  // Clean up temporary image files
776
781
  await cleanupTempFiles(tempImagePaths, tempDir);
777
782
 
778
- // Send completion event
779
- console.log('Streaming complete, sending claude-complete event');
780
- ws.send({
781
- type: 'claude-complete',
782
- sessionId: capturedSessionId,
783
- exitCode: 0,
784
- isNewSession: !sessionId && !!command
785
- });
786
- console.log('claude-complete event sent');
783
+ // Only send completion event if not aborted — when the session is aborted
784
+ // via abortClaudeSDKSession(), that function already sends 'session-aborted'
785
+ // to the frontend, so we must not send a duplicate 'claude-complete'.
786
+ if (!abortController.signal.aborted) {
787
+ console.log('Streaming complete, sending claude-complete event');
788
+ ws.send({
789
+ type: 'claude-complete',
790
+ sessionId: capturedSessionId,
791
+ exitCode: 0,
792
+ isNewSession: !sessionId && !!command
793
+ });
794
+ console.log('claude-complete event sent');
795
+ } else {
796
+ console.log('Session was aborted, skipping claude-complete event');
797
+ }
787
798
 
788
799
  } catch (error) {
800
+ // If the session was aborted, this is expected — do not treat as an error.
801
+ if (abortController.signal.aborted) {
802
+ console.log('Session aborted, ignoring post-abort error:', error.message);
803
+ if (capturedSessionId) {
804
+ removeSession(capturedSessionId);
805
+ }
806
+ await cleanupTempFiles(tempImagePaths, tempDir);
807
+ return;
808
+ }
809
+
789
810
  console.error('SDK query error:', error);
790
811
 
791
812
  // Clean up session on error
@@ -822,8 +843,20 @@ async function abortClaudeSDKSession(sessionId) {
822
843
  try {
823
844
  console.log(`Aborting SDK session: ${sessionId}`);
824
845
 
825
- // Call interrupt() on the query instance
826
- await session.instance.interrupt();
846
+ // Signal abort via AbortController (stops SDK internal operations)
847
+ if (session.abortController) {
848
+ session.abortController.abort();
849
+ }
850
+
851
+ // Forcefully close the query and terminate the underlying process.
852
+ // close() is the correct method for aborting a running query — it kills the
853
+ // CLI subprocess and cleans up all resources (MCP transports, pending requests).
854
+ // interrupt() only works in streaming-input mode and is not reliable here.
855
+ try {
856
+ session.instance.close();
857
+ } catch (closeError) {
858
+ console.error(`Error closing session ${sessionId}:`, closeError);
859
+ }
827
860
 
828
861
  // Update session status
829
862
  session.status = 'aborted';
@@ -78,6 +78,8 @@ export async function evaluate(toolName, input, { userUuid, cwd }) {
78
78
  const rewrittenInput = rewriteClaudePaths(toolName, input, claudeDir);
79
79
  const effectiveInput = rewrittenInput || input;
80
80
 
81
+ if (VERBOSE) console.log(`[ToolGuard] INPUT ${toolName}:`, JSON.stringify(effectiveInput));
82
+
81
83
  if (rewrittenInput && VERBOSE) {
82
84
  console.log(`[ToolGuard] PATH-REWRITE ${toolName}: .claude paths → ${claudeDir}`);
83
85
  }