agent-tower 0.5.2-beta.1 → 0.5.2-beta.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.
Files changed (84) hide show
  1. package/dist/mcp/http-client.d.ts +8 -2
  2. package/dist/mcp/http-client.d.ts.map +1 -1
  3. package/dist/mcp/http-client.js +4 -4
  4. package/dist/mcp/http-client.js.map +1 -1
  5. package/dist/mcp/server.d.ts.map +1 -1
  6. package/dist/mcp/server.js +48 -5
  7. package/dist/mcp/server.js.map +1 -1
  8. package/dist/routes/team-runs.d.ts.map +1 -1
  9. package/dist/routes/team-runs.js +92 -2
  10. package/dist/routes/team-runs.js.map +1 -1
  11. package/dist/services/__tests__/team-reconciler.service.test.js +171 -3
  12. package/dist/services/__tests__/team-reconciler.service.test.js.map +1 -1
  13. package/dist/services/__tests__/team-run.service.test.js +87 -0
  14. package/dist/services/__tests__/team-run.service.test.js.map +1 -1
  15. package/dist/services/__tests__/team-scheduler.service.test.js +131 -0
  16. package/dist/services/__tests__/team-scheduler.service.test.js.map +1 -1
  17. package/dist/services/session-manager.d.ts.map +1 -1
  18. package/dist/services/session-manager.js +11 -0
  19. package/dist/services/session-manager.js.map +1 -1
  20. package/dist/services/team-run.service.d.ts +16 -0
  21. package/dist/services/team-run.service.d.ts.map +1 -1
  22. package/dist/services/team-run.service.js +196 -4
  23. package/dist/services/team-run.service.js.map +1 -1
  24. package/dist/services/team-scheduler.service.d.ts +8 -4
  25. package/dist/services/team-scheduler.service.d.ts.map +1 -1
  26. package/dist/services/team-scheduler.service.js +26 -9
  27. package/dist/services/team-scheduler.service.js.map +1 -1
  28. package/dist/utils/process-launch.d.ts.map +1 -1
  29. package/dist/utils/process-launch.js +52 -9
  30. package/dist/utils/process-launch.js.map +1 -1
  31. package/dist/utils/process-launch.test.js +65 -1
  32. package/dist/utils/process-launch.test.js.map +1 -1
  33. package/dist/web/assets/{AgentDemoPage-CkhVcgZo.js → AgentDemoPage-Co63rtls.js} +1 -1
  34. package/dist/web/assets/{DemoPage-dkmp8Vvn.js → DemoPage-PD8AX5pI.js} +1 -1
  35. package/dist/web/assets/{GeneralSettingsPage-B4DH6g3D.js → GeneralSettingsPage-C5eiEeTZ.js} +1 -1
  36. package/dist/web/assets/{MemberAvatar-pwugXGL4.js → MemberAvatar-BDXmryjB.js} +1 -1
  37. package/dist/web/assets/{NotificationSettingsPage-C-bPU_ZE.js → NotificationSettingsPage-BjjPan2M.js} +1 -1
  38. package/dist/web/assets/{ProfileSettingsPage-Dlr6BmHb.js → ProfileSettingsPage-BRpeNq6u.js} +1 -1
  39. package/dist/web/assets/ProjectKanbanPage-DUUu2EYy.js +89 -0
  40. package/dist/web/assets/{ProjectSettingsPage-DZZksq5G.js → ProjectSettingsPage-avctMKfk.js} +1 -1
  41. package/dist/web/assets/{ProviderSettingsPage-BWN7CEG6.js → ProviderSettingsPage-BIUYN2e0.js} +1 -1
  42. package/dist/web/assets/{SettingsSection-C-sMhXpf.js → SettingsSection-pLJ3msrT.js} +1 -1
  43. package/dist/web/assets/{TeamSettingsPage-924xpocx.js → TeamSettingsPage-BjKW9nT8.js} +1 -1
  44. package/dist/web/assets/{arrow-left-UHjQiY5K.js → arrow-left-lFz8nyZM.js} +1 -1
  45. package/dist/web/assets/{button-CUTjpRqw.js → button-BUA8P726.js} +1 -1
  46. package/dist/web/assets/check-BYuuXc71.js +1 -0
  47. package/dist/web/assets/{chevron-down-Bby7sJEv.js → chevron-down-B931AgE2.js} +1 -1
  48. package/dist/web/assets/{chevron-right-DCC0lyoB.js → chevron-right-F9i4wey6.js} +1 -1
  49. package/dist/web/assets/{chevron-up-BnCoaejn.js → chevron-up-BEHjZ-SB.js} +1 -1
  50. package/dist/web/assets/{circle-check-BscClK07.js → circle-check-C7xXtl8B.js} +1 -1
  51. package/dist/web/assets/{code-block-OCS4YCEC-DkMlYSza.js → code-block-OCS4YCEC-6FHTyCN2.js} +1 -1
  52. package/dist/web/assets/{confirm-dialog-wsb35VpE.js → confirm-dialog-B_wk_zqm.js} +1 -1
  53. package/dist/web/assets/{folder-picker-B-X_nrS1.js → folder-picker-BEHS3b2Q.js} +1 -1
  54. package/dist/web/assets/index-DEQhT5sD.css +1 -0
  55. package/dist/web/assets/{index-CUJoWIuo.js → index-RxbHMWXV.js} +7 -7
  56. package/dist/web/assets/{loader-circle-Cul4BuAa.js → loader-circle-stJcjnm-.js} +1 -1
  57. package/dist/web/assets/{mermaid-NOHMQCX5-DoPzf-UA.js → mermaid-NOHMQCX5-fubZbKyF.js} +3 -3
  58. package/dist/web/assets/{message-square-C7Q71jFj.js → message-square-C8lb-TDo.js} +1 -1
  59. package/dist/web/assets/{modal-25-qs8P5.js → modal-UGrjU63G.js} +1 -1
  60. package/dist/web/assets/{pencil-CaJR6Dqm.js → pencil-uRBgB4qA.js} +1 -1
  61. package/dist/web/assets/{rotate-ccw-Bvz7590n.js → rotate-ccw-CWaMrTz4.js} +1 -1
  62. package/dist/web/assets/{select-CzeTYLO4.js → select-BMlH2AuL.js} +1 -1
  63. package/dist/web/assets/{upload-_2T21rVP.js → upload-B-Mpvu9j.js} +1 -1
  64. package/dist/web/assets/{use-profiles-BaCwGP06.js → use-profiles-D6cZwU1R.js} +1 -1
  65. package/dist/web/assets/{use-providers-Bwl7R5Ql.js → use-providers-DVP6emIi.js} +1 -1
  66. package/dist/web/index.html +2 -2
  67. package/node_modules/@agent-tower/shared/dist/socket/events.d.ts +1 -1
  68. package/node_modules/@agent-tower/shared/dist/socket/events.d.ts.map +1 -1
  69. package/node_modules/@agent-tower/shared/dist/types.d.ts +4 -1
  70. package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
  71. package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
  72. package/node_modules/@prisma/client/.prisma/client/edge.js +6 -5
  73. package/node_modules/@prisma/client/.prisma/client/index-browser.js +1 -0
  74. package/node_modules/@prisma/client/.prisma/client/index.d.ts +43 -0
  75. package/node_modules/@prisma/client/.prisma/client/index.js +6 -5
  76. package/node_modules/@prisma/client/.prisma/client/package.json +1 -1
  77. package/node_modules/@prisma/client/.prisma/client/schema.prisma +2 -0
  78. package/node_modules/@prisma/client/.prisma/client/wasm.js +1 -0
  79. package/package.json +1 -1
  80. package/prisma/migrations/20260609000000_add_team_member_membership_status/migration.sql +3 -0
  81. package/prisma/schema.prisma +2 -0
  82. package/dist/web/assets/ProjectKanbanPage-7oEfjjWV.js +0 -89
  83. package/dist/web/assets/check-CPkZgPjx.js +0 -1
  84. package/dist/web/assets/index-4hNWw0yi.css +0 -1
@@ -122,7 +122,7 @@ function createRouteSchedulerMock() {
122
122
  }
123
123
  return asAgentInvocations(invocations);
124
124
  });
125
- scheduler.approveWorkRequestAndStartNext = vi.fn(async (workRequestId) => {
125
+ scheduler.approveWorkRequestAndStartNext = vi.fn(async (workRequestId, _options) => {
126
126
  const workRequest = await prisma.workRequest.update({
127
127
  where: { id: workRequestId },
128
128
  data: { status: 'QUEUED' },
@@ -130,7 +130,7 @@ function createRouteSchedulerMock() {
130
130
  const startedInvocations = await scheduler.startNextSessions(workRequest.teamRunId);
131
131
  return { workRequest: asWorkRequest(workRequest), startedInvocations };
132
132
  });
133
- scheduler.rejectWorkRequest = vi.fn(async (workRequestId) => prisma.workRequest.update({
133
+ scheduler.rejectWorkRequest = vi.fn(async (workRequestId, _options) => prisma.workRequest.update({
134
134
  where: { id: workRequestId },
135
135
  data: { status: 'REJECTED' },
136
136
  }).then(asWorkRequest));
@@ -1463,6 +1463,15 @@ describe('TeamReconcilerService', () => {
1463
1463
  });
1464
1464
  expect(noMentionResult.isError).toBe(true);
1465
1465
  expect(getMcpToolText(noMentionResult)).toContain('mentionMembers');
1466
+ const noStopResult = await callToolForCurrentMember(noRead, noReadInvocation.id, {
1467
+ name: 'stop_member_work',
1468
+ arguments: {
1469
+ member_id: noRead.members[1].id,
1470
+ cancel_queued: true,
1471
+ },
1472
+ });
1473
+ expect(noStopResult.isError).toBe(true);
1474
+ expect(getMcpToolText(noStopResult)).toContain('stopMemberWork');
1466
1475
  await expect(prisma.roomMessage.count({
1467
1476
  where: {
1468
1477
  content: {
@@ -1512,6 +1521,24 @@ describe('TeamReconcilerService', () => {
1512
1521
  status: 'QUEUED',
1513
1522
  instruction: 'Other queued request',
1514
1523
  });
1524
+ const ownPendingApproval = await createWorkRequest({
1525
+ teamRunId: teamRun.id,
1526
+ targetMemberId: members[0].id,
1527
+ status: 'PENDING_APPROVAL',
1528
+ instruction: 'Own pending approval',
1529
+ });
1530
+ const ownPendingRejection = await createWorkRequest({
1531
+ teamRunId: teamRun.id,
1532
+ targetMemberId: members[0].id,
1533
+ status: 'PENDING_APPROVAL',
1534
+ instruction: 'Own pending rejection',
1535
+ });
1536
+ const otherPendingApproval = await createWorkRequest({
1537
+ teamRunId: teamRun.id,
1538
+ targetMemberId: members[1].id,
1539
+ status: 'PENDING_APPROVAL',
1540
+ instruction: 'Other pending approval',
1541
+ });
1515
1542
  const app = Fastify({ logger: false });
1516
1543
  try {
1517
1544
  setTeamRunEnv({
@@ -1569,6 +1596,33 @@ describe('TeamReconcilerService', () => {
1569
1596
  });
1570
1597
  expect(forbiddenCancel.isError).toBe(true);
1571
1598
  expect(getMcpToolText(forbiddenCancel)).toContain('FORBIDDEN');
1599
+ await expect(prisma.workRequest.findUnique({ where: { id: otherRequest.id } })).resolves.toMatchObject({
1600
+ status: 'QUEUED',
1601
+ });
1602
+ const approveResult = await client.callTool({
1603
+ name: 'approve_work_request',
1604
+ arguments: { work_request_id: ownPendingApproval.id },
1605
+ });
1606
+ expect(approveResult.isError, getMcpToolText(approveResult)).not.toBe(true);
1607
+ expect(JSON.parse(getMcpToolText(approveResult)).workRequest).toMatchObject({
1608
+ id: ownPendingApproval.id,
1609
+ status: 'QUEUED',
1610
+ });
1611
+ const rejectResult = await client.callTool({
1612
+ name: 'reject_work_request',
1613
+ arguments: { work_request_id: ownPendingRejection.id },
1614
+ });
1615
+ expect(rejectResult.isError, getMcpToolText(rejectResult)).not.toBe(true);
1616
+ expect(JSON.parse(getMcpToolText(rejectResult))).toMatchObject({
1617
+ id: ownPendingRejection.id,
1618
+ status: 'REJECTED',
1619
+ });
1620
+ const forbiddenApprove = await client.callTool({
1621
+ name: 'approve_work_request',
1622
+ arguments: { work_request_id: otherPendingApproval.id },
1623
+ });
1624
+ expect(forbiddenApprove.isError).toBe(true);
1625
+ expect(getMcpToolText(forbiddenApprove)).toContain('FORBIDDEN');
1572
1626
  }
1573
1627
  finally {
1574
1628
  await client.close();
@@ -1578,7 +1632,13 @@ describe('TeamReconcilerService', () => {
1578
1632
  status: 'CANCELLED',
1579
1633
  });
1580
1634
  await expect(prisma.workRequest.findUnique({ where: { id: otherRequest.id } })).resolves.toMatchObject({
1581
- status: 'QUEUED',
1635
+ status: 'STARTED',
1636
+ });
1637
+ await expect(prisma.workRequest.findUnique({ where: { id: ownPendingRejection.id } })).resolves.toMatchObject({
1638
+ status: 'REJECTED',
1639
+ });
1640
+ await expect(prisma.workRequest.findUnique({ where: { id: otherPendingApproval.id } })).resolves.toMatchObject({
1641
+ status: 'PENDING_APPROVAL',
1582
1642
  });
1583
1643
  }
1584
1644
  finally {
@@ -1712,6 +1772,114 @@ describe('TeamReconcilerService', () => {
1712
1772
  await app.close();
1713
1773
  }
1714
1774
  });
1775
+ it('passes requester member scope for REST WorkRequest approval and rejection', async () => {
1776
+ const { teamRun, members } = await createFixture({ memberCount: 2, teamRunMode: 'CONFIRM' });
1777
+ const ownPending = await createWorkRequest({
1778
+ teamRunId: teamRun.id,
1779
+ targetMemberId: members[0].id,
1780
+ status: 'PENDING_APPROVAL',
1781
+ instruction: 'Own approval',
1782
+ });
1783
+ const otherPending = await createWorkRequest({
1784
+ teamRunId: teamRun.id,
1785
+ targetMemberId: members[1].id,
1786
+ status: 'PENDING_APPROVAL',
1787
+ instruction: 'Other rejection',
1788
+ });
1789
+ const routeScheduler = createRouteSchedulerMock();
1790
+ const app = Fastify({ logger: false });
1791
+ try {
1792
+ await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
1793
+ const approve = await app.inject({
1794
+ method: 'POST',
1795
+ url: `/api/team-runs/work-requests/${ownPending.id}/approve`,
1796
+ payload: {
1797
+ teamRunId: teamRun.id,
1798
+ requesterMemberId: members[0].id,
1799
+ },
1800
+ });
1801
+ expect(approve.statusCode).toBe(200);
1802
+ expect(routeScheduler.approveWorkRequestAndStartNext).toHaveBeenCalledWith(ownPending.id, {
1803
+ teamRunId: teamRun.id,
1804
+ requesterMemberId: members[0].id,
1805
+ });
1806
+ const reject = await app.inject({
1807
+ method: 'POST',
1808
+ url: `/api/team-runs/work-requests/${otherPending.id}/reject`,
1809
+ payload: {
1810
+ teamRunId: teamRun.id,
1811
+ requesterMemberId: members[0].id,
1812
+ },
1813
+ });
1814
+ expect(reject.statusCode).toBe(200);
1815
+ expect(routeScheduler.rejectWorkRequest).toHaveBeenCalledWith(otherPending.id, {
1816
+ teamRunId: teamRun.id,
1817
+ requesterMemberId: members[0].id,
1818
+ });
1819
+ }
1820
+ finally {
1821
+ await app.close();
1822
+ }
1823
+ });
1824
+ it('does not soft-remove a member when active stop fails during removal', async () => {
1825
+ const { workspace, teamRun, members } = await createFixture({ memberCount: 2 });
1826
+ const activeRequest = await createWorkRequest({
1827
+ teamRunId: teamRun.id,
1828
+ targetMemberId: members[0].id,
1829
+ status: 'STARTED',
1830
+ instruction: 'Active work',
1831
+ });
1832
+ const queuedRequest = await createWorkRequest({
1833
+ teamRunId: teamRun.id,
1834
+ targetMemberId: members[0].id,
1835
+ status: 'QUEUED',
1836
+ instruction: 'Queued work',
1837
+ });
1838
+ await createRunningInvocation({
1839
+ teamRunId: teamRun.id,
1840
+ workRequestId: activeRequest.id,
1841
+ memberId: members[0].id,
1842
+ workspaceId: workspace.id,
1843
+ status: 'RUNNING',
1844
+ });
1845
+ const routeScheduler = createRouteSchedulerMock();
1846
+ routeScheduler.stopMemberWork = vi.fn(async () => {
1847
+ throw new Error('stop failed');
1848
+ });
1849
+ const app = Fastify({ logger: false });
1850
+ try {
1851
+ await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
1852
+ const response = await app.inject({
1853
+ method: 'POST',
1854
+ url: `/api/team-runs/${teamRun.id}/members/${members[0].id}/remove`,
1855
+ payload: {
1856
+ stopActive: true,
1857
+ cancelQueued: true,
1858
+ },
1859
+ });
1860
+ expect(response.statusCode).toBe(500);
1861
+ expect(routeScheduler.stopMemberWork).toHaveBeenCalledWith(teamRun.id, members[0].id, {
1862
+ cancelQueued: true,
1863
+ });
1864
+ await expect(prisma.teamMember.findUnique({ where: { id: members[0].id } })).resolves.toMatchObject({
1865
+ membershipStatus: 'ACTIVE',
1866
+ status: 'IDLE',
1867
+ });
1868
+ await expect(prisma.workRequest.findUnique({ where: { id: queuedRequest.id } })).resolves.toMatchObject({
1869
+ status: 'QUEUED',
1870
+ });
1871
+ await expect(prisma.agentInvocation.count({
1872
+ where: {
1873
+ teamRunId: teamRun.id,
1874
+ memberId: members[0].id,
1875
+ status: 'RUNNING',
1876
+ },
1877
+ })).resolves.toBe(1);
1878
+ }
1879
+ finally {
1880
+ await app.close();
1881
+ }
1882
+ });
1715
1883
  it('auto-starts USER_MESSAGES work when a user posts an unmentioned room message', async () => {
1716
1884
  const { teamRun, members } = await createFixture({
1717
1885
  memberCount: 2,