agent-gear 1.1.5 → 1.1.6

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.
@@ -9,6 +9,25 @@ const { mockStart, mockStop, MockHeartbeat } = vi.hoisted(() => {
9
9
  return { mockStart: start, mockStop: stop, MockHeartbeat: HB };
10
10
  });
11
11
 
12
+ const { MockAcpSyncBridge, mockBridge } = vi.hoisted(() => {
13
+ const bridge = {
14
+ connect: vi.fn().mockResolvedValue(undefined),
15
+ disconnect: vi.fn().mockResolvedValue(undefined),
16
+ listSessions: vi.fn(),
17
+ deleteSession: vi.fn(),
18
+ supportsDelete: vi.fn(),
19
+ start: vi.fn(),
20
+ stop: vi.fn(),
21
+ resume: vi.fn(),
22
+ };
23
+ const Cls = vi.fn(function () { return bridge; });
24
+ return { MockAcpSyncBridge: Cls, mockBridge: bridge };
25
+ });
26
+
27
+ vi.mock('../src/acp/sync-bridge.js', () => ({
28
+ AcpSyncBridge: MockAcpSyncBridge,
29
+ }));
30
+
12
31
  vi.mock('socket.io-client', () => ({
13
32
  io: vi.fn(),
14
33
  }));
@@ -316,4 +335,196 @@ describe('SocketIO Client', () => {
316
335
  });
317
336
  });
318
337
  });
338
+
339
+ describe('acp:list-sessions', () => {
340
+ function setup() {
341
+ mockBridge.connect.mockClear();
342
+ mockBridge.disconnect.mockClear();
343
+ mockBridge.listSessions.mockClear();
344
+ const mockSocket = createMockSocket();
345
+ vi.mocked(io).mockReturnValue(mockSocket as never);
346
+ connectToServer('http://localhost:4001', 'zag_k_1_abc123');
347
+ mockSocket._trigger('connect');
348
+ mockSocket._trigger('registered', { controllerId: 'ctrl-1' });
349
+ return mockSocket;
350
+ }
351
+
352
+ it('should pass cursor and emit nextCursor in result', async () => {
353
+ mockBridge.listSessions.mockResolvedValue({
354
+ sessions: [{ sessionId: 'hist-2', title: 'Session 2', cwd: '/home/user', updatedAt: null }],
355
+ nextCursor: 'cursor-2',
356
+ });
357
+ const mockSocket = setup();
358
+
359
+ mockSocket._trigger('acp:list-sessions', {
360
+ requestId: 'req-1',
361
+ serverType: 'opencode',
362
+ cwd: '/home/user',
363
+ cursor: 'cursor-1',
364
+ });
365
+
366
+ await vi.waitFor(() => {
367
+ expect(mockBridge.listSessions).toHaveBeenCalledWith('/home/user', 'cursor-1');
368
+ expect(mockSocket.emit).toHaveBeenCalledWith('acp:list-sessions:result', {
369
+ requestId: 'req-1',
370
+ success: true,
371
+ sessions: [{ sessionId: 'hist-2', title: 'Session 2', cwd: '/home/user', updatedAt: null }],
372
+ nextCursor: 'cursor-2',
373
+ });
374
+ });
375
+ });
376
+
377
+ it('should omit nextCursor when absent', async () => {
378
+ mockBridge.listSessions.mockResolvedValue({
379
+ sessions: [{ sessionId: 'hist-1', title: 'Session 1', cwd: '/home/user', updatedAt: null }],
380
+ });
381
+ const mockSocket = setup();
382
+
383
+ mockSocket._trigger('acp:list-sessions', {
384
+ requestId: 'req-2',
385
+ serverType: 'opencode',
386
+ cwd: '/home/user',
387
+ });
388
+
389
+ await vi.waitFor(() => {
390
+ expect(mockBridge.listSessions).toHaveBeenCalledWith('/home/user', undefined);
391
+ expect(mockSocket.emit).toHaveBeenCalledWith('acp:list-sessions:result', {
392
+ requestId: 'req-2',
393
+ success: true,
394
+ sessions: [{ sessionId: 'hist-1', title: 'Session 1', cwd: '/home/user', updatedAt: null }],
395
+ });
396
+ });
397
+ });
398
+
399
+ it('should emit failure result on error', async () => {
400
+ mockBridge.listSessions.mockRejectedValue(new Error('boom'));
401
+ const mockSocket = setup();
402
+
403
+ mockSocket._trigger('acp:list-sessions', {
404
+ requestId: 'req-3',
405
+ serverType: 'opencode',
406
+ cwd: '/home/user',
407
+ });
408
+
409
+ await vi.waitFor(() => {
410
+ expect(mockSocket.emit).toHaveBeenCalledWith('acp:list-sessions:result', {
411
+ requestId: 'req-3',
412
+ success: false,
413
+ error: 'boom',
414
+ });
415
+ });
416
+ });
417
+ });
418
+
419
+ describe('acp:set-config-option', () => {
420
+ it('should emit failure result when bridge not found', async () => {
421
+ const mockSocket = createMockSocket();
422
+ vi.mocked(io).mockReturnValue(mockSocket as never);
423
+ connectToServer('http://localhost:4001', 'zag_k_1_abc123');
424
+ mockSocket._trigger('connect');
425
+ mockSocket._trigger('registered', { controllerId: 'ctrl-1' });
426
+
427
+ mockSocket._trigger('acp:set-config-option', {
428
+ sessionId: 'unknown-sess',
429
+ configId: 'opt-1',
430
+ value: 'val-1',
431
+ });
432
+
433
+ await vi.waitFor(() => {
434
+ expect(mockSocket.emit).toHaveBeenCalledWith('acp:set-config-option:result', {
435
+ sessionId: 'unknown-sess',
436
+ success: false,
437
+ configId: 'opt-1',
438
+ error: 'ACP bridge not found for session',
439
+ });
440
+ });
441
+ });
442
+ });
443
+
444
+ describe('acp:delete-history-session', () => {
445
+ function setup() {
446
+ mockBridge.connect.mockClear();
447
+ mockBridge.disconnect.mockClear();
448
+ mockBridge.deleteSession.mockClear();
449
+ mockBridge.supportsDelete.mockClear();
450
+ const mockSocket = createMockSocket();
451
+ vi.mocked(io).mockReturnValue(mockSocket as never);
452
+ connectToServer('http://localhost:4001', 'zag_k_1_abc123');
453
+ mockSocket._trigger('connect');
454
+ mockSocket._trigger('registered', { controllerId: 'ctrl-1' });
455
+ return mockSocket;
456
+ }
457
+
458
+ it('should delete session via sync bridge when supported', async () => {
459
+ mockBridge.supportsDelete.mockReturnValue(true);
460
+ mockBridge.deleteSession.mockResolvedValue(undefined);
461
+ const mockSocket = setup();
462
+
463
+ mockSocket._trigger('acp:delete-history-session', {
464
+ requestId: 'req-1',
465
+ sessionId: 'db-id-1',
466
+ externalSessionId: 'ext-1',
467
+ cwd: '/home/user',
468
+ serverType: 'opencode',
469
+ });
470
+
471
+ await vi.waitFor(() => {
472
+ expect(mockBridge.connect).toHaveBeenCalled();
473
+ expect(mockBridge.supportsDelete).toHaveBeenCalled();
474
+ expect(mockBridge.deleteSession).toHaveBeenCalledWith('ext-1');
475
+ expect(mockSocket.emit).toHaveBeenCalledWith('acp:delete-history-session:result', {
476
+ requestId: 'req-1',
477
+ sessionId: 'db-id-1',
478
+ success: true,
479
+ supported: true,
480
+ });
481
+ });
482
+ });
483
+
484
+ it('should report unsupported without deleting when agent lacks capability', async () => {
485
+ mockBridge.supportsDelete.mockReturnValue(false);
486
+ const mockSocket = setup();
487
+
488
+ mockSocket._trigger('acp:delete-history-session', {
489
+ requestId: 'req-2',
490
+ sessionId: 'db-id-2',
491
+ externalSessionId: 'ext-2',
492
+ cwd: '/home/user',
493
+ });
494
+
495
+ await vi.waitFor(() => {
496
+ expect(mockBridge.deleteSession).not.toHaveBeenCalled();
497
+ expect(mockSocket.emit).toHaveBeenCalledWith('acp:delete-history-session:result', {
498
+ requestId: 'req-2',
499
+ sessionId: 'db-id-2',
500
+ success: false,
501
+ supported: false,
502
+ error: 'Session delete not supported by agent',
503
+ });
504
+ });
505
+ });
506
+
507
+ it('should emit failure result on error', async () => {
508
+ mockBridge.supportsDelete.mockReturnValue(true);
509
+ mockBridge.deleteSession.mockRejectedValue(new Error('boom'));
510
+ const mockSocket = setup();
511
+
512
+ mockSocket._trigger('acp:delete-history-session', {
513
+ requestId: 'req-3',
514
+ sessionId: 'db-id-3',
515
+ externalSessionId: 'ext-3',
516
+ cwd: '/home/user',
517
+ });
518
+
519
+ await vi.waitFor(() => {
520
+ expect(mockSocket.emit).toHaveBeenCalledWith('acp:delete-history-session:result', {
521
+ requestId: 'req-3',
522
+ sessionId: 'db-id-3',
523
+ success: false,
524
+ supported: true,
525
+ error: 'boom',
526
+ });
527
+ });
528
+ });
529
+ });
319
530
  });